-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an option to newContext to disable memoization of global context …
…template. For Python Isolate Pools we want to instantiate emscripten in a separate context on isolate creation (before we have compatibility flags) and later after receiving the compatibility flags create a new context. One blocker to this approach is all the templates that are instantiated in the first context are memoized. This PR allows us to create a new context without memoizing the global context templates. This feature is off by default and will be enabled in a future PR for python isolates only.
- Loading branch information
Showing
4 changed files
with
312 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
#include <workerd/io/compatibility-date.h> | ||
#include <workerd/io/observer.h> | ||
#include <workerd/jsg/jsg.h> | ||
#include <workerd/jsg/setup.h> | ||
|
||
#include <capnp/message.h> | ||
#include <kj/test.h> | ||
|
||
namespace workerd::jsg::test { | ||
jsg::V8System v8System; | ||
|
||
struct TestApi1: public jsg::Object { | ||
TestApi1() = default; | ||
TestApi1(jsg::Lock&, const jsg::Url&) {} | ||
int test1(jsg::Lock& js) { | ||
return 1; | ||
} | ||
|
||
int test2(jsg::Lock& js) { | ||
return 2; | ||
} | ||
static jsg::Ref<TestApi1> constructor() { | ||
return jsg::alloc<TestApi1>(); | ||
} | ||
|
||
JSG_RESOURCE_TYPE(TestApi1, workerd::CompatibilityFlags::Reader flags) { | ||
if (flags.getPythonWorkers()) { | ||
JSG_METHOD(test2); | ||
} else { | ||
JSG_METHOD(test1); | ||
} | ||
} | ||
}; | ||
struct TestApi2: public jsg::Object { | ||
TestApi2() = default; | ||
TestApi2(jsg::Lock&, const jsg::Url&) {} | ||
int test1(jsg::Lock& js) { | ||
return 1; | ||
} | ||
|
||
int test2(jsg::Lock& js) { | ||
return 2; | ||
} | ||
static jsg::Ref<TestApi2> constructor() { | ||
return jsg::alloc<TestApi2>(); | ||
} | ||
|
||
JSG_RESOURCE_TYPE(TestApi2, workerd::CompatibilityFlags::Reader flags) { | ||
if (flags.getPythonWorkers()) { | ||
JSG_METHOD(test2); | ||
} else { | ||
JSG_METHOD(test1); | ||
} | ||
} | ||
}; | ||
|
||
struct BaseTestContext: public jsg::Object, public jsg::ContextGlobal { | ||
int test1(jsg::Lock& js) { | ||
return 1; | ||
} | ||
|
||
int test2(jsg::Lock& js) { | ||
return 2; | ||
} | ||
JSG_RESOURCE_TYPE(BaseTestContext, workerd::CompatibilityFlags::Reader flags) { | ||
if (flags.getPythonWorkers()) { | ||
JSG_METHOD(test2); | ||
} else { | ||
JSG_METHOD(test1); | ||
} | ||
JSG_NESTED_TYPE(TestApi1); | ||
} | ||
}; | ||
|
||
struct TestContext: public BaseTestContext { | ||
int test3(jsg::Lock& js) { | ||
return 3; | ||
} | ||
|
||
int test4(jsg::Lock& js) { | ||
return 4; | ||
} | ||
JSG_RESOURCE_TYPE(TestContext, workerd::CompatibilityFlags::Reader flags) { | ||
JSG_INHERIT(BaseTestContext); | ||
if (flags.getPythonWorkers()) { | ||
JSG_METHOD(test4); | ||
} else { | ||
JSG_METHOD(test3); | ||
} | ||
JSG_NESTED_TYPE(TestApi2); | ||
} | ||
}; | ||
|
||
JSG_DECLARE_ISOLATE_TYPE(TestIsolate, TestContext, BaseTestContext, TestApi1, TestApi2); | ||
|
||
class Configuration { | ||
public: | ||
Configuration(workerd::CompatibilityFlags::Reader& flags): flags(flags) {} | ||
operator const workerd::CompatibilityFlags::Reader() const { | ||
return flags; | ||
} | ||
|
||
private: | ||
workerd::CompatibilityFlags::Reader& flags; | ||
}; | ||
|
||
void expectEval( | ||
jsg::Lock& js, kj::StringPtr code, kj::StringPtr expectedType, kj::StringPtr expectedValue) { | ||
// Create a string containing the JavaScript source code. | ||
v8::Local<v8::String> source = jsg::v8Str(js.v8Isolate, code); | ||
|
||
// Compile the source code. | ||
v8::Local<v8::Script> script; | ||
if (!v8::Script::Compile(js.v8Context(), source).ToLocal(&script)) { | ||
KJ_FAIL_EXPECT("code didn't parse", code); | ||
return; | ||
} | ||
|
||
v8::TryCatch catcher(js.v8Isolate); | ||
|
||
// Run the script to get the result. | ||
v8::Local<v8::Value> result; | ||
if (script->Run(js.v8Context()).ToLocal(&result)) { | ||
v8::String::Utf8Value type(js.v8Isolate, result->TypeOf(js.v8Isolate)); | ||
v8::String::Utf8Value value(js.v8Isolate, result); | ||
|
||
KJ_EXPECT(*type == expectedType, *type, expectedType); | ||
KJ_EXPECT(*value == expectedValue, *value, expectedValue); | ||
} else if (catcher.HasCaught()) { | ||
v8::String::Utf8Value message(js.v8Isolate, catcher.Exception()); | ||
|
||
KJ_EXPECT(expectedType == "throws", expectedType, catcher.Exception()); | ||
KJ_EXPECT(*message == expectedValue, *message, expectedValue); | ||
} else { | ||
KJ_FAIL_EXPECT("returned empty handle but didn't throw exception?"); | ||
} | ||
} | ||
|
||
KJ_TEST("Create a context with memoization disabled change flags then create another context") { | ||
auto observer = kj::atomicRefcounted<workerd::IsolateObserver>(); | ||
capnp::MallocMessageBuilder flagsArena; | ||
auto flags = flagsArena.initRoot<::workerd::CompatibilityFlags>(); | ||
auto flagsReader = flags.asReader(); | ||
Configuration config(flagsReader); | ||
TestIsolate isolate(v8System, config, kj::atomicAddRef(*observer)); | ||
isolate.runInLockScope([&](TestIsolate::Lock& lock) { | ||
jsg::JsContext<TestContext> context = lock.newContext<TestContext, false>(); | ||
v8::Local<v8::Context> ctx = context.getHandle(lock); | ||
KJ_ASSERT(!ctx.IsEmpty(), "unable to enter invalid v8::Context"); | ||
v8::Context::Scope scope(ctx); | ||
|
||
expectEval(lock, "test1()", "number", "1"); | ||
expectEval(lock, "test2()", "throws", "ReferenceError: test2 is not defined"); | ||
expectEval(lock, "test3()", "number", "3"); | ||
expectEval(lock, "test4()", "throws", "ReferenceError: test4 is not defined"); | ||
expectEval(lock, "new TestApi1().test1()", "number", "1"); | ||
expectEval(lock, "new TestApi1().test2()", "throws", | ||
"TypeError: (intermediate value).test2 is not a function"); | ||
expectEval(lock, "new TestApi2().test1()", "number", "1"); | ||
expectEval(lock, "new TestApi2().test2()", "throws", | ||
"TypeError: (intermediate value).test2 is not a function"); | ||
}); | ||
flags.setPythonWorkers(true); | ||
isolate.runInLockScope([&](TestIsolate::Lock& lock) { | ||
jsg::JsContext<TestContext> context = lock.newContext<TestContext>(); | ||
v8::Local<v8::Context> ctx = context.getHandle(lock); | ||
KJ_ASSERT(!ctx.IsEmpty(), "unable to enter invalid v8::Context"); | ||
v8::Context::Scope scope(ctx); | ||
|
||
expectEval(lock, "test1()", "throws", "ReferenceError: test1 is not defined"); | ||
expectEval(lock, "test2()", "number", "2"); | ||
expectEval(lock, "test3()", "throws", "ReferenceError: test3 is not defined"); | ||
expectEval(lock, "test4()", "number", "4"); | ||
expectEval(lock, "new TestApi1().test1()", "throws", | ||
"TypeError: (intermediate value).test1 is not a function"); | ||
expectEval(lock, "new TestApi1().test2()", "number", "2"); | ||
expectEval(lock, "new TestApi2().test1()", "throws", | ||
"TypeError: (intermediate value).test1 is not a function"); | ||
expectEval(lock, "new TestApi2().test2()", "number", "2"); | ||
}); | ||
} | ||
|
||
} // namespace workerd::jsg::test |
Oops, something went wrong.