// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/builtins/builtins-utils-inl.h" #include "src/codegen/compiler.h" #include "src/logging/counters.h" #include "src/objects/js-shadow-realm-inl.h" namespace v8 { namespace internal { // https://tc39.es/proposal-shadowrealm/#sec-shadowrealm-constructor BUILTIN(ShadowRealmConstructor) { HandleScope scope(isolate); // 1. If NewTarget is undefined, throw a TypeError exception. if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, isolate->factory()->ShadowRealm_string())); } // [[Construct]] Handle target = args.target(); Handle new_target = Handle::cast(args.new_target()); // 3. Let realmRec be CreateRealm(). // 5. Let context be a new execution context. // 6. Set the Function of context to null. // 7. Set the Realm of context to realmRec. // 8. Set the ScriptOrModule of context to null. // 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined). // 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]). // 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]). // These steps are combined in // Isolate::RunHostCreateShadowRealmContextCallback and Context::New. // The host operation is hoisted for not creating a half-initialized // ShadowRealm object, which can fail the heap verification. Handle native_context; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, native_context, isolate->RunHostCreateShadowRealmContextCallback()); // 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget, // "%ShadowRealm.prototype%", « [[ShadowRealm]], [[ExecutionContext]] »). Handle result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, result, JSObject::New(target, new_target, Handle::null())); Handle O = Handle::cast(result); // 4. Set O.[[ShadowRealm]] to realmRec. // 9. Set O.[[ExecutionContext]] to context. O->set_native_context(*native_context); // 13. Return O. return *O; } namespace { // https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue MaybeHandle GetWrappedValue(Isolate* isolate, Handle creation_context, Handle value) { // 1. If Type(value) is Object, then if (!value->IsJSReceiver()) { // 2. Return value. return value; } // 1a. If IsCallable(value) is false, throw a TypeError exception. if (!value->IsCallable()) { // The TypeError thrown is created with creation Realm's TypeError // constructor instead of the executing Realm's. THROW_NEW_ERROR_RETURN_VALUE( isolate, NewError(Handle(creation_context->type_error_function(), isolate), MessageTemplate::kNotCallable, value), {}); } // 1b. Return ? WrappedFunctionCreate(callerRealm, value). return JSWrappedFunction::Create(isolate, creation_context, Handle::cast(value)); } } // namespace // https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.evaluate BUILTIN(ShadowRealmPrototypeEvaluate) { HandleScope scope(isolate); Handle source_text = args.atOrUndefined(isolate, 1); // 1. Let O be this value. Handle receiver = args.receiver(); Factory* factory = isolate->factory(); // 2. Perform ? ValidateShadowRealmObject(O). if (!receiver->IsJSShadowRealm()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver)); } Handle shadow_realm = Handle::cast(receiver); // 3. If Type(sourceText) is not String, throw a TypeError exception. if (!source_text->IsString()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kInvalidShadowRealmEvaluateSourceText)); } // 4. Let callerRealm be the current Realm Record. Handle caller_context = isolate->native_context(); // 5. Let evalRealm be O.[[ShadowRealm]]. Handle eval_context = Handle(shadow_realm->native_context(), isolate); // 6. Return ? PerformShadowRealmEval(sourceText, callerRealm, evalRealm). // PerformShadowRealmEval // https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval // 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm). // Run embedder pre-checks before executing the source code. MaybeHandle validated_source; bool unhandled_object; std::tie(validated_source, unhandled_object) = Compiler::ValidateDynamicCompilationSource(isolate, eval_context, source_text); if (unhandled_object) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kInvalidShadowRealmEvaluateSourceText)); } Handle eval_global_proxy(eval_context->global_proxy(), isolate); MaybeHandle result; bool is_parse_failed = false; { // 8. If runningContext is not already suspended, suspend runningContext. // 9. Let evalContext be a new ECMAScript code execution context. // 10. Set evalContext's Function to null. // 11. Set evalContext's Realm to evalRealm. // 12. Set evalContext's ScriptOrModule to null. // 13. Set evalContext's VariableEnvironment to varEnv. // 14. Set evalContext's LexicalEnvironment to lexEnv. // 15. Push evalContext onto the execution context stack; evalContext is now // the running execution context. SaveAndSwitchContext save(isolate, *eval_context); // 2. Perform the following substeps in an implementation-defined order, // possibly interleaving parsing and error detection: // 2a. Let script be ParseText(! StringToCodePoints(sourceText), Script). // 2b. If script is a List of errors, throw a SyntaxError exception. // 2c. If script Contains ScriptBody is false, return undefined. // 2d. Let body be the ScriptBody of script. // 2e. If body Contains NewTarget is true, throw a SyntaxError // exception. // 2f. If body Contains SuperProperty is true, throw a SyntaxError // exception. // 2g. If body Contains SuperCall is true, throw a SyntaxError exception. // 3. Let strictEval be IsStrict of script. // 4. Let runningContext be the running execution context. // 5. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]). // 6. Let varEnv be evalRealm.[[GlobalEnv]]. // 7. If strictEval is true, set varEnv to lexEnv. Handle function; MaybeHandle maybe_function = Compiler::GetFunctionFromValidatedString(eval_context, validated_source, NO_PARSE_RESTRICTION, kNoSourcePosition); if (maybe_function.is_null()) { is_parse_failed = true; } else { function = maybe_function.ToHandleChecked(); // 16. Let result be EvalDeclarationInstantiation(body, varEnv, // lexEnv, null, strictEval). // 17. If result.[[Type]] is normal, then // 20a. Set result to the result of evaluating body. // 18. If result.[[Type]] is normal and result.[[Value]] is empty, then // 21a. Set result to NormalCompletion(undefined). result = Execution::Call(isolate, function, eval_global_proxy, 0, nullptr); // 19. Suspend evalContext and remove it from the execution context stack. // 20. Resume the context that is now on the top of the execution context // stack as the running execution context. Done by the scope. } } if (result.is_null()) { DCHECK(isolate->has_pending_exception()); Handle pending_exception = Handle(isolate->pending_exception(), isolate); isolate->clear_pending_exception(); if (is_parse_failed) { Handle error_object = Handle::cast(pending_exception); Handle message = Handle::cast(JSReceiver::GetDataProperty( isolate, error_object, factory->message_string())); return isolate->ReThrow( *factory->NewError(isolate->syntax_error_function(), message)); } // 21. If result.[[Type]] is not normal, throw a TypeError exception. // TODO(v8:11989): provide a non-observable inspection on the // pending_exception to the newly created TypeError. // https://github.com/tc39/proposal-shadowrealm/issues/353 THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kCallShadowRealmFunctionThrown)); } // 22. Return ? GetWrappedValue(callerRealm, result.[[Value]]). Handle wrapped_result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, wrapped_result, GetWrappedValue(isolate, caller_context, result.ToHandleChecked())); return *wrapped_result; } } // namespace internal } // namespace v8