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) {
PerIsolateData* data = PerIsolateData::Get(isolate);
int index = data->RealmIndexOrThrow(args, 0);
if (index == -1) return;
if (args.Length() < 2 || !args[1]->IsString()) {
args.GetIsolate()->ThrowError("Invalid argument");
if (args.Length() < 2) {
isolate->ThrowError("Invalid argument");
return;
}
Local<String> source;
if (!ReadSource(args, 1, CodeType::kString).ToLocal(&source)) {
isolate->ThrowError("Invalid argument");
return;
}
ScriptOrigin origin(isolate,
String::NewFromUtf8Literal(isolate, "(d8)",
NewStringType::kInternalized));
ScriptCompiler::Source script_source(
args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin);
ScriptCompiler::Source script_source(source, origin);
Local<UnboundScript> script;
if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
.ToLocal(&script)) {
......@@ -2236,35 +2241,33 @@ void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
PerIsolateData::Get(isolate)->SetTimeout(callback, context);
}
enum WorkerType { kClassic, kString, kFunction, kInvalid, kNone };
void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args,
WorkerType* worker_type,
Local<Value>* arguments = nullptr) {
void Shell::ReadCodeTypeAndArguments(
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
CodeType* code_type, Local<Value>* arguments) {
Isolate* isolate = args.GetIsolate();
if (args.Length() > 1 && args[1]->IsObject()) {
Local<Object> object = args[1].As<Object>();
if (args.Length() > index && args[index]->IsObject()) {
Local<Object> object = args[index].As<Object>();
Local<Context> context = isolate->GetCurrentContext();
Local<Value> value;
if (!TryGetValue(isolate, context, object, "type").ToLocal(&value)) {
*worker_type = WorkerType::kNone;
*code_type = CodeType::kNone;
return;
}
if (!value->IsString()) {
*worker_type = WorkerType::kInvalid;
*code_type = CodeType::kInvalid;
return;
}
Local<String> worker_type_string =
value->ToString(context).ToLocalChecked();
String::Utf8Value str(isolate, worker_type_string);
if (strcmp("string", *str) == 0) {
*worker_type = WorkerType::kString;
} else if (strcmp("classic", *str) == 0) {
*worker_type = WorkerType::kClassic;
if (strcmp("classic", *str) == 0) {
*code_type = CodeType::kFileName;
} else if (strcmp("string", *str) == 0) {
*code_type = CodeType::kString;
} else if (strcmp("function", *str) == 0) {
*worker_type = WorkerType::kFunction;
*code_type = CodeType::kFunction;
} else {
*worker_type = WorkerType::kInvalid;
*code_type = CodeType::kInvalid;
}
if (arguments != nullptr) {
bool got_arguments =
......@@ -2272,13 +2275,14 @@ void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args,
USE(got_arguments);
}
} else {
*worker_type = WorkerType::kNone;
*code_type = CodeType::kNone;
}
}
bool FunctionAndArgumentsToString(Local<Function> function,
Local<Value> arguments, Local<String>* source,
Isolate* isolate) {
bool Shell::FunctionAndArgumentsToString(Local<Function> function,
Local<Value> arguments,
Local<String>* source,
Isolate* isolate) {
Local<Context> context = isolate->GetCurrentContext();
MaybeLocal<String> maybe_function_string =
function->FunctionProtoToString(context);
......@@ -2321,6 +2325,54 @@ bool FunctionAndArgumentsToString(Local<Function> function,
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) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
......@@ -2330,45 +2382,9 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
Local<String> source;
if (args[0]->IsFunction()) {
// d8 supports `options={type: 'function', arguments:[...]}`, which means
// 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;
}
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 (!ReadSource(args, 0, CodeType::kFileName).ToLocal(&source)) {
isolate->ThrowError("Invalid argument");
return;
}
if (!args.IsConstructCall()) {
......
......@@ -443,6 +443,7 @@ class Shell : public i::AllStatic {
kProcessMessageQueue = true,
kNoProcessMessageQueue = false
};
enum class CodeType { kFileName, kString, kFunction, kInvalid, kNone };
static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, PrintResult print_result,
......@@ -539,6 +540,16 @@ class Shell : public i::AllStatic {
static void WriteChars(const char* name, uint8_t* buffer, size_t buffer_size);
static void ExecuteFile(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 WorkerPostMessage(
const v8::FunctionCallbackInfo<v8::Value>& args);
......
......@@ -4,28 +4,22 @@
// Flags: --experimental-d8-web-snapshot-api
function callString(f) {
return '(' + f.toString() + ')()';
}
function use() {
function use(exports) {
const result = Object.create(null);
Realm.shared.exports.forEach(x => result[x] = globalThis[x]);
exports.forEach(x => result[x] = globalThis[x]);
return result;
}
function takeAndUseWebSnapshot(createObjects, exports) {
// Make the exports list available across Realms.
Realm.shared = { exports };
// Take a snapshot in Realm r1.
const r1 = Realm.create();
Realm.eval(r1, callString(createObjects));
Realm.eval(r1, createObjects, {type: 'function'});
const snapshot = Realm.takeWebSnapshot(r1, exports);
// Use the snapshot in Realm r2.
const r2 = Realm.create();
const success = Realm.useWebSnapshot(r2, snapshot);
assertTrue(success);
return Realm.eval(r2, callString(use));
return Realm.eval(r2, use, {type: 'function', arguments: [exports]});
}
(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