blob: fb509e3fef1f3bde5bde4cd4b949943acde12065 [file] [log] [blame]
// 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 (IsUndefined(*args.new_target(), isolate)) { // [[Call]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
isolate->factory()->ShadowRealm_string()));
}
// [[Construct]]
DirectHandle<JSFunction> target = args.target();
DirectHandle<JSReceiver> new_target = Cast<JSReceiver>(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.
DirectHandle<NativeContext> native_context;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, native_context,
isolate->RunHostCreateShadowRealmContextCallback());
// 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
// "%ShadowRealm.prototype%", « [[ShadowRealm]], [[ExecutionContext]] »).
DirectHandle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
JSObject::New(target, new_target, {}));
auto O = Cast<JSShadowRealm>(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
MaybeDirectHandle<Object> GetWrappedValue(
Isolate* isolate, DirectHandle<NativeContext> creation_context,
Handle<Object> value) {
// 1. If Type(value) is Object, then
if (!IsJSReceiver(*value)) {
// 2. Return value.
return value;
}
// 1a. If IsCallable(value) is false, throw a TypeError exception.
if (!IsCallable(*value)) {
// The TypeError thrown is created with creation Realm's TypeError
// constructor instead of the executing Realm's.
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewError(
direct_handle(creation_context->type_error_function(), isolate),
MessageTemplate::kNotCallable, value),
{});
}
// 1b. Return ? WrappedFunctionCreate(callerRealm, value).
return JSWrappedFunction::Create(isolate, creation_context,
Cast<JSReceiver>(value));
}
} // namespace
// https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.evaluate
BUILTIN(ShadowRealmPrototypeEvaluate) {
HandleScope scope(isolate);
Handle<Object> source_text = args.atOrUndefined(isolate, 1);
// 1. Let O be this value.
DirectHandle<Object> receiver = args.receiver();
Factory* factory = isolate->factory();
// 2. Perform ? ValidateShadowRealmObject(O).
if (!IsJSShadowRealm(*receiver)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver));
}
auto shadow_realm = Cast<JSShadowRealm>(receiver);
// 3. If Type(sourceText) is not String, throw a TypeError exception.
if (!IsString(*source_text)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kInvalidShadowRealmEvaluateSourceText));
}
// 4. Let callerRealm be the current Realm Record.
DirectHandle<NativeContext> caller_context = isolate->native_context();
// 5. Let evalRealm be O.[[ShadowRealm]].
DirectHandle<NativeContext> eval_context(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.
MaybeDirectHandle<String> 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));
}
DirectHandle<JSObject> eval_global_proxy(eval_context->global_proxy(),
isolate);
MaybeHandle<Object> 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. Set evalContext's PrivateEnvironment to null.
// 16. 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.
DirectHandle<JSFunction> function;
MaybeDirectHandle<JSFunction> 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();
// 17. Let result be EvalDeclarationInstantiation(body, varEnv,
// lexEnv, null, strictEval).
// 18. If result.[[Type]] is normal, then
// 18a. a. Set result to Completion(Evaluation of body).
// 19. If result.[[Type]] is normal and result.[[Value]] is empty, then
// 19a. Set result to NormalCompletion(undefined).
result = Execution::Call(isolate, function, eval_global_proxy, {});
// 20. Suspend evalContext and remove it from the execution context stack.
// 21. 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_exception());
Handle<Object> exception(isolate->exception(), isolate);
isolate->clear_internal_exception();
if (is_parse_failed) {
auto error_object = Cast<JSObject>(exception);
auto message = Cast<String>(JSReceiver::GetDataProperty(
isolate, error_object, factory->message_string()));
return isolate->ReThrow(
*factory->NewError(isolate->syntax_error_function(), message));
}
// 22. If result.[[Type]] is not NORMAL, then
// 22a. Let copiedError be CreateTypeErrorCopy(callerRealm,
// result.[[Value]]). 22b. Return ThrowCompletion(copiedError).
DirectHandle<String> string =
Object::NoSideEffectsToString(isolate, exception);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
ShadowRealmNewTypeErrorCopy(
exception, MessageTemplate::kCallShadowRealmEvaluateThrew, string));
}
// 23. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
DirectHandle<Object> wrapped_result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, wrapped_result,
GetWrappedValue(isolate, caller_context, result.ToHandleChecked()));
return *wrapped_result;
}
} // namespace internal
} // namespace v8