Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions componentize.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ aot=@AOT@
preopen_dir="${PREOPEN_DIR:-}"

usage() {
echo "Usage: $(basename "$0") [--verbose] [--legacy-script] [input.js] [-o output.wasm]"
echo "Usage: $(basename "$0") [--verbose] [-i,--initializer-script-path] [--legacy-script] [input.js] [-o output.wasm]"
echo " Providing an input file but no output uses the input base name with a .wasm extension"
echo " Providing an output file but no input creates a component without running any top-level script"
echo " Specifying '--verbose' causes the detailed output during initialization and execution"
echo " Specifying '-i' or '--initializer-script-path' allows specifying an initializer script"
echo " Specifying '--legacy-script' causes evaluation as a legacy JS script instead of a module"
exit 1
}
Expand All @@ -24,6 +26,7 @@ fi
IN_FILE=""
OUT_FILE=""
LEGACY_SCRIPT_PARAM=""
STARLING_ARGS=""
VERBOSE=0

while [ $# -gt 0 ]
Expand All @@ -38,7 +41,12 @@ do
OUT_FILE="$2"
shift 2
;;
-i|--initializer-script-path)
STARLING_ARGS="$STARLING_ARGS $1 $2"
shift 2
;;
-v|--verbose)
STARLING_ARGS="$1 $STARLING_ARGS"
VERBOSE=1
shift
;;
Expand Down Expand Up @@ -76,9 +84,8 @@ if [[ -n "$IN_FILE" ]]; then
fi
echo "Componentizing $IN_FILE into $OUT_FILE"

STARLING_ARGS="$LEGACY_SCRIPT_PARAM$IN_FILE"
STARLING_ARGS="$STARLING_ARGS $LEGACY_SCRIPT_PARAM$IN_FILE"
if [[ $VERBOSE -ne 0 ]]; then
STARLING_ARGS="--verbose $STARLING_ARGS"
echo "Componentizing with args $STARLING_ARGS"
fi

Expand Down
6 changes: 6 additions & 0 deletions include/config-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ class ConfigParser {
config_->content_script_path.reset();
i++;
}
}
if (args[i] == "-i" || args[i] == "--initializer-script-path") {
if (i + 1 < args.size()) {
config_->initializer_script_path = mozilla::Some(args[i + 1]);
i++;
}
} else if (args[i] == "-v" || args[i] == "--verbose") {
config_->verbose = true;
} else if (args[i] == "-d" || args[i] == "--enable-script-debugging") {
Expand Down
22 changes: 20 additions & 2 deletions include/extension-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,19 @@ namespace api {
class AsyncTask;

struct EngineConfig {
mozilla::Maybe<std::string> content_script_path;
mozilla::Maybe<std::string> content_script;
mozilla::Maybe<std::string> content_script_path = mozilla::Nothing();
mozilla::Maybe<std::string> content_script = mozilla::Nothing();
bool module_mode = true;

/**
* Path to the script to evaluate before the content script.
*
* This script is evaluated in a separate global and has access to functions not
* available to content. It can be used to set up the environment for the content
* script, e.g. by registering builtin modules or adding global properties.
*/
mozilla::Maybe<std::string> initializer_script_path = mozilla::Nothing();

/**
* Whether to evaluate the top-level script in pre-initialization mode or not.
*
Expand Down Expand Up @@ -102,6 +111,15 @@ class Engine {
bool eval_toplevel(JS::SourceText<mozilla::Utf8Unit> &source, const char *path,
MutableHandleValue result);

/**
* Run the script set using the `-i | --initializer-script-path` option.
*
* This script runs in a separate global, and has access to functions not
* available to content. Notably, that includes the ability to define
* builtin modules, using the `defineBuiltinModule` function.
*/
bool run_initialization_script();

/**
* Run the async event loop as long as there's interest registered in keeping it running.
*
Expand Down
4 changes: 2 additions & 2 deletions runtime/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static bool print_location(JSContext *cx, FILE *fp = stdout) {
return true;
}

static bool dbg_print(JSContext *cx, unsigned argc, Value *vp) {
bool content_debugger::dbg_print(JSContext *cx, unsigned argc, Value *vp) {
CallArgs args = CallArgsFromVp(argc, vp);

if (!print_location(cx)) {
Expand Down Expand Up @@ -231,7 +231,7 @@ bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_init
}

if (!JS_DefineFunction(cx, global, "setContentPath", dbg_set_content_path, 1, 0) ||
!JS_DefineFunction(cx, global, "print", dbg_print, 1, 0) ||
!JS_DefineFunction(cx, global, "print", content_debugger::dbg_print, 1, 0) ||
!JS_DefineFunction(cx, global, "assert", dbg_assert, 1, 0)) {
return false;
}
Expand Down
2 changes: 2 additions & 0 deletions runtime/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace content_debugger {
* @return the path to the replacement script, if any
*/
mozilla::Maybe<std::string_view> replacement_script_path();

bool dbg_print(JSContext *cx, unsigned argc, Value *vp);
} // namespace content_debugger

#endif // DEBUGGER_H
64 changes: 64 additions & 0 deletions runtime/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,12 @@ Engine::Engine(std::unique_ptr<EngineConfig> config) {
}
#endif

if (config_->initializer_script_path) {
if (!run_initialization_script()) {
abort("running initialization script");
}
}

TRACE("Module mode: " << config_->module_mode);
scriptLoader->enable_module_mode(config_->module_mode);

Expand Down Expand Up @@ -493,9 +499,67 @@ void Engine::abort(const char *reason) {
}

bool Engine::define_builtin_module(const char* id, HandleValue builtin) {
TRACE("Defining builtin module '" << id << "'");
return scriptLoader->define_builtin_module(id, builtin);
}

static bool define_builtin_module(JSContext *cx, unsigned argc, Value *vp) {
CallArgs args = CallArgsFromVp(argc, vp);
auto name = core::encode(cx, args.get(0));
if (!name) {
return false;
}
if (!args.get(1).isObject()) {
JS_ReportErrorUTF8(cx, "Second argument to defineBuiltinModule must be an object");
return false;
}
if (!Engine::get(cx)->define_builtin_module(name.ptr.get(), args.get(1))) {
return false;
}

args.rval().setUndefined();
return true;
}

bool Engine::run_initialization_script() {
auto cx = this->cx();

JS::RealmOptions options;
options.creationOptions()
.setStreamsEnabled(true)
.setExistingCompartment(this->global());

static JSClass global_class = {"global", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps};
RootedObject global(cx);
global = JS_NewGlobalObject(cx, &global_class, nullptr, JS::DontFireOnNewGlobalHook, options);
if (!global) {
return false;
}

JSAutoRealm ar(cx, global);

if (!JS_DefineFunction(cx, global, "defineBuiltinModule", ::define_builtin_module, 2, 0) ||
!JS_DefineFunction(cx, global, "print", content_debugger::dbg_print, 1, 0)) {
return false;
}

auto path = config_->initializer_script_path.value();
TRACE("Running initialization script from file " << path);
JS::SourceText<mozilla::Utf8Unit> source;
if (!scriptLoader->load_resolved_script(CONTEXT, path.c_str(), path.c_str(), source)) {
return false;
}
auto opts = new JS::CompileOptions(cx);
opts->setDiscardSource();
opts->setFile(path.c_str());
JS::RootedScript script(cx, Compile(cx, *opts, source));
if (!script) {
return false;
}
RootedValue result(cx);
return JS_ExecuteScript(cx, script, &result);
}

bool Engine::eval_toplevel(JS::SourceText<mozilla::Utf8Unit> &source, const char *path,
MutableHandleValue result) {
MOZ_ASSERT(state() > EngineState::EngineInitializing, "Engine must be done initializing");
Expand Down
12 changes: 5 additions & 7 deletions runtime/script_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,6 @@ static const char* resolve_path(const char* path, const char* base, size_t base_
return resolve_extension(resolved_path);
}

static bool load_script(JSContext *cx, const char *script_path, const char* resolved_path,
JS::SourceText<mozilla::Utf8Unit> &script);

static JSObject* get_module(JSContext* cx, JS::SourceText<mozilla::Utf8Unit> &source,
const char* resolved_path, const JS::CompileOptions &opts) {
RootedObject module(cx, JS::CompileModule(cx, opts, source));
Expand Down Expand Up @@ -185,7 +182,7 @@ static JSObject* get_module(JSContext* cx, const char* specifier, const char* re
}

JS::SourceText<mozilla::Utf8Unit> source;
if (!load_script(cx, specifier, resolved_path, source)) {
if (!SCRIPT_LOADER->load_resolved_script(cx, specifier, resolved_path, source)) {
return nullptr;
}

Expand Down Expand Up @@ -387,8 +384,9 @@ void ScriptLoader::enable_module_mode(bool enable) {
MODULE_MODE = enable;
}

static bool load_script(JSContext *cx, const char *specifier, const char* resolved_path,
JS::SourceText<mozilla::Utf8Unit> &script) {
bool ScriptLoader::load_resolved_script(JSContext *cx, const char *specifier,
const char* resolved_path,
JS::SourceText<mozilla::Utf8Unit> &script) {
FILE *file = fopen(resolved_path, "r");
if (!file) {
std::cerr << "Error opening file " << specifier << " (resolved to " << resolved_path << "): "
Expand Down Expand Up @@ -442,7 +440,7 @@ bool ScriptLoader::load_script(JSContext *cx, const char *script_path,
resolved_path = resolve_path(script_path, BASE_PATH, strlen(BASE_PATH));
}

return ::load_script(cx, script_path, resolved_path, script);
return load_resolved_script(cx, script_path, resolved_path, script);
}

bool ScriptLoader::eval_top_level_script(const char *path, JS::SourceText<mozilla::Utf8Unit> &source,
Expand Down
9 changes: 9 additions & 0 deletions runtime/script_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ class ScriptLoader {
bool eval_top_level_script(const char *path, JS::SourceText<mozilla::Utf8Unit> &source,
MutableHandleValue result, MutableHandleValue tla_promise);
bool load_script(JSContext* cx, const char *script_path, JS::SourceText<mozilla::Utf8Unit> &script);

/**
* Load a script without attempting to resolve its path relative to a base path.
*
* This is useful for loading ancillary scripts without interfering with, or depending on,
* the script loader's state as determined by loading and running content scripts.
*/
bool load_resolved_script(JSContext *cx, const char *specifier, const char *resolved_path,
JS::SourceText<mozilla::Utf8Unit> &script);
};

#endif //SCRIPTLOADER_H
1 change: 1 addition & 0 deletions tests/e2e/init-script/expect_serve_stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stdout [0] :: Log: foo
8 changes: 8 additions & 0 deletions tests/e2e/init-script/init-script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { func } from "builtinMod";
async function handle(event) {
console.log(func());
return new Response(func());
}

//@ts-ignore
addEventListener('fetch', (event) => { event.respondWith(handle(event)) });
8 changes: 8 additions & 0 deletions tests/e2e/init-script/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const builtinMod = {
func() {
return 'foo';
}
}

defineBuiltinModule('builtinMod', builtinMod);
print("initialization done");
7 changes: 6 additions & 1 deletion tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,15 @@ print_diff_content_on_fail() {
# Optionally create the test component if not explicitly provided
if [ -z "$test_component" ]; then
test_component="$test_dir/$test_name.wasm"
runtime_args="$test_dir/$test_name.js"
# If the test directory has an init.js, prepend it to the runtime args
if [ -f "$test_dir/init.js" ]; then
runtime_args="-i $test_dir/init.js $runtime_args"
fi

# Run Wizer
set +e
PREOPEN_DIR="$(dirname $(dirname "$test_dir"))" "$test_runtime/componentize.sh" $componentize_flags "$test_dir/$test_name.js" "$test_component" 1> "$stdout_log" 2> "$stderr_log"
PREOPEN_DIR="$(dirname $(dirname "$test_dir"))" "$test_runtime/componentize.sh" $componentize_flags $runtime_args "$test_component" 1> "$stdout_log" 2> "$stderr_log"
wizer_result=$?
set -e

Expand Down
1 change: 1 addition & 0 deletions tests/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ test_e2e(tla)
test_e2e(stream-forwarding)
test_e2e(multi-stream-forwarding)
test_e2e(teed-stream-as-outgoing-body)
test_e2e(init-script)

test_integration(blob)
test_integration(btoa)
Expand Down