Commit 385f304d authored by Vicky Kontoura's avatar Vicky Kontoura Committed by V8 LUCI CQ

[d8] Support more ways of passing source code to Realm.eval()

This CL updates Realm.eval() to also handle reading source code as a
JavaScript function or from a file. To distinguish between different
argument types, an additional options bag needs to be provided. If no
options bag is provided, the behavior defaults to the current one,
which is reading source code from a string.

Bug: v8:11525, v8:11706
Change-Id: I68238335eb91171041dca2c83db211c40dd68359
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2944435Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Vicky Kontoura <vkont@google.com>
Cr-Commit-Position: refs/heads/master@{#75021}
parent 08ce6e5c
...@@ -1787,15 +1787,20 @@ void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1787,15 +1787,20 @@ void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
PerIsolateData* data = PerIsolateData::Get(isolate); PerIsolateData* data = PerIsolateData::Get(isolate);
int index = data->RealmIndexOrThrow(args, 0); int index = data->RealmIndexOrThrow(args, 0);
if (index == -1) return; if (index == -1) return;
if (args.Length() < 2 || !args[1]->IsString()) { if (args.Length() < 2) {
args.GetIsolate()->ThrowError("Invalid argument"); isolate->ThrowError("Invalid argument");
return;
}
Local<String> source;
if (!ReadSource(args, 1, CodeType::kString).ToLocal(&source)) {
isolate->ThrowError("Invalid argument");
return; return;
} }
ScriptOrigin origin(isolate, ScriptOrigin origin(isolate,
String::NewFromUtf8Literal(isolate, "(d8)", String::NewFromUtf8Literal(isolate, "(d8)",
NewStringType::kInternalized)); NewStringType::kInternalized));
ScriptCompiler::Source script_source( ScriptCompiler::Source script_source(source, origin);
args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin);
Local<UnboundScript> script; Local<UnboundScript> script;
if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source) if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
.ToLocal(&script)) { .ToLocal(&script)) {
...@@ -2236,35 +2241,33 @@ void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -2236,35 +2241,33 @@ void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
PerIsolateData::Get(isolate)->SetTimeout(callback, context); PerIsolateData::Get(isolate)->SetTimeout(callback, context);
} }
enum WorkerType { kClassic, kString, kFunction, kInvalid, kNone }; void Shell::ReadCodeTypeAndArguments(
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args, CodeType* code_type, Local<Value>* arguments) {
WorkerType* worker_type,
Local<Value>* arguments = nullptr) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
if (args.Length() > 1 && args[1]->IsObject()) { if (args.Length() > index && args[index]->IsObject()) {
Local<Object> object = args[1].As<Object>(); Local<Object> object = args[index].As<Object>();
Local<Context> context = isolate->GetCurrentContext(); Local<Context> context = isolate->GetCurrentContext();
Local<Value> value; Local<Value> value;
if (!TryGetValue(isolate, context, object, "type").ToLocal(&value)) { if (!TryGetValue(isolate, context, object, "type").ToLocal(&value)) {
*worker_type = WorkerType::kNone; *code_type = CodeType::kNone;
return; return;
} }
if (!value->IsString()) { if (!value->IsString()) {
*worker_type = WorkerType::kInvalid; *code_type = CodeType::kInvalid;
return; return;
} }
Local<String> worker_type_string = Local<String> worker_type_string =
value->ToString(context).ToLocalChecked(); value->ToString(context).ToLocalChecked();
String::Utf8Value str(isolate, worker_type_string); String::Utf8Value str(isolate, worker_type_string);
if (strcmp("string", *str) == 0) { if (strcmp("classic", *str) == 0) {
*worker_type = WorkerType::kString; *code_type = CodeType::kFileName;
} else if (strcmp("classic", *str) == 0) { } else if (strcmp("string", *str) == 0) {
*worker_type = WorkerType::kClassic; *code_type = CodeType::kString;
} else if (strcmp("function", *str) == 0) { } else if (strcmp("function", *str) == 0) {
*worker_type = WorkerType::kFunction; *code_type = CodeType::kFunction;
} else { } else {
*worker_type = WorkerType::kInvalid; *code_type = CodeType::kInvalid;
} }
if (arguments != nullptr) { if (arguments != nullptr) {
bool got_arguments = bool got_arguments =
...@@ -2272,12 +2275,13 @@ void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args, ...@@ -2272,12 +2275,13 @@ void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args,
USE(got_arguments); USE(got_arguments);
} }
} else { } else {
*worker_type = WorkerType::kNone; *code_type = CodeType::kNone;
} }
} }
bool FunctionAndArgumentsToString(Local<Function> function, bool Shell::FunctionAndArgumentsToString(Local<Function> function,
Local<Value> arguments, Local<String>* source, Local<Value> arguments,
Local<String>* source,
Isolate* isolate) { Isolate* isolate) {
Local<Context> context = isolate->GetCurrentContext(); Local<Context> context = isolate->GetCurrentContext();
MaybeLocal<String> maybe_function_string = MaybeLocal<String> maybe_function_string =
...@@ -2321,6 +2325,54 @@ bool FunctionAndArgumentsToString(Local<Function> function, ...@@ -2321,6 +2325,54 @@ bool FunctionAndArgumentsToString(Local<Function> function,
return true; return true;
} }
// ReadSource() supports reading source code through `args[index]` as specified
// by the `default_type` or an optional options bag provided in `args[index+1]`
// (e.g. `options={type: 'code_type', arguments:[...]}`).
MaybeLocal<String> Shell::ReadSource(
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
CodeType default_type) {
CodeType code_type;
Local<Value> arguments;
ReadCodeTypeAndArguments(args, index + 1, &code_type, &arguments);
Isolate* isolate = args.GetIsolate();
Local<String> source;
if (code_type == CodeType::kNone) {
code_type = default_type;
}
switch (code_type) {
case CodeType::kFunction:
if (!args[index]->IsFunction()) {
return MaybeLocal<String>();
}
// Source: ( function_to_string )( params )
if (!FunctionAndArgumentsToString(args[index].As<Function>(), arguments,
&source, isolate)) {
return MaybeLocal<String>();
}
break;
case CodeType::kFileName: {
if (!args[index]->IsString()) {
return MaybeLocal<String>();
}
String::Utf8Value filename(isolate, args[index]);
source = Shell::ReadFile(isolate, *filename);
if (source.IsEmpty()) return MaybeLocal<String>();
break;
}
case CodeType::kString:
if (!args[index]->IsString()) {
return MaybeLocal<String>();
}
source = args[index].As<String>();
break;
case CodeType::kNone:
case CodeType::kInvalid:
return MaybeLocal<String>();
}
return source;
}
void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
...@@ -2330,47 +2382,11 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -2330,47 +2382,11 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
Local<String> source; Local<String> source;
if (args[0]->IsFunction()) { if (!ReadSource(args, 0, CodeType::kFileName).ToLocal(&source)) {
// d8 supports `options={type: 'function', arguments:[...]}`, which means isolate->ThrowError("Invalid argument");
// the first argument is a function with the code to be ran. Restrictions
// apply; in particular the function will be converted to a string and the
// Worker constructed based on it.
WorkerType worker_type;
Local<Value> arguments;
ReadWorkerTypeAndArguments(args, &worker_type, &arguments);
if (worker_type != WorkerType::kFunction) {
isolate->ThrowError("Invalid or missing worker type");
return;
}
// Source: ( function_to_string )( params )
if (!FunctionAndArgumentsToString(args[0].As<Function>(), arguments,
&source, isolate)) {
return;
}
} else {
// d8 honors `options={type: 'string'}`, which means the first argument is
// not a filename but string of script to be run.
bool load_from_file = true;
WorkerType worker_type;
ReadWorkerTypeAndArguments(args, &worker_type);
if (worker_type == WorkerType::kString) {
load_from_file = false;
} else if (worker_type != WorkerType::kNone &&
worker_type != WorkerType::kClassic) {
isolate->ThrowError("Invalid worker type");
return; return;
} }
if (load_from_file) {
String::Utf8Value filename(isolate, args[0]);
source = ReadFile(isolate, *filename);
if (source.IsEmpty()) return;
} else {
source = args[0].As<String>();
}
}
if (!args.IsConstructCall()) { if (!args.IsConstructCall()) {
isolate->ThrowError("Worker must be constructed with new"); isolate->ThrowError("Worker must be constructed with new");
return; return;
......
...@@ -443,6 +443,7 @@ class Shell : public i::AllStatic { ...@@ -443,6 +443,7 @@ class Shell : public i::AllStatic {
kProcessMessageQueue = true, kProcessMessageQueue = true,
kNoProcessMessageQueue = false kNoProcessMessageQueue = false
}; };
enum class CodeType { kFileName, kString, kFunction, kInvalid, kNone };
static bool ExecuteString(Isolate* isolate, Local<String> source, static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, PrintResult print_result, Local<Value> name, PrintResult print_result,
...@@ -539,6 +540,16 @@ class Shell : public i::AllStatic { ...@@ -539,6 +540,16 @@ class Shell : public i::AllStatic {
static void WriteChars(const char* name, uint8_t* buffer, size_t buffer_size); static void WriteChars(const char* name, uint8_t* buffer, size_t buffer_size);
static void ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args); static void ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args); static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ReadCodeTypeAndArguments(
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
CodeType* code_type, Local<Value>* arguments = nullptr);
static bool FunctionAndArgumentsToString(Local<Function> function,
Local<Value> arguments,
Local<String>* source,
Isolate* isolate);
static MaybeLocal<String> ReadSource(
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
CodeType default_type);
static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args); static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerPostMessage( static void WorkerPostMessage(
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
......
...@@ -4,28 +4,22 @@ ...@@ -4,28 +4,22 @@
// Flags: --experimental-d8-web-snapshot-api // Flags: --experimental-d8-web-snapshot-api
function callString(f) { function use(exports) {
return '(' + f.toString() + ')()';
}
function use() {
const result = Object.create(null); const result = Object.create(null);
Realm.shared.exports.forEach(x => result[x] = globalThis[x]); exports.forEach(x => result[x] = globalThis[x]);
return result; return result;
} }
function takeAndUseWebSnapshot(createObjects, exports) { function takeAndUseWebSnapshot(createObjects, exports) {
// Make the exports list available across Realms.
Realm.shared = { exports };
// Take a snapshot in Realm r1. // Take a snapshot in Realm r1.
const r1 = Realm.create(); const r1 = Realm.create();
Realm.eval(r1, callString(createObjects)); Realm.eval(r1, createObjects, {type: 'function'});
const snapshot = Realm.takeWebSnapshot(r1, exports); const snapshot = Realm.takeWebSnapshot(r1, exports);
// Use the snapshot in Realm r2. // Use the snapshot in Realm r2.
const r2 = Realm.create(); const r2 = Realm.create();
const success = Realm.useWebSnapshot(r2, snapshot); const success = Realm.useWebSnapshot(r2, snapshot);
assertTrue(success); assertTrue(success);
return Realm.eval(r2, callString(use)); return Realm.eval(r2, use, {type: 'function', arguments: [exports]});
} }
(function TestMinimal() { (function TestMinimal() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment