Commit 44a387ef authored by Hiroshige Hayashizaki's avatar Hiroshige Hayashizaki Committed by Commit Bot

Reland "Introduce ScriptEvaluationResult"

This reverts commit 44541a20.

Reason for revert:

The bot failure should be fixed by fixing a DCHECK() in
worker_or_worklet_script_controller.cc that contained
side effects. (crbug.com/1132893)

Original change's description:
> Revert "Introduce ScriptEvaluationResult"
>
> This reverts commit 3a4677f6.
>
> Reason for revert: bot failures due to crash at blink::AnimationWorkletGlobalScopeTest::RunScriptAndGetBoolean()
> see https://crbug.com/chromium/1132893
>
> Original change's description:
> > Introduce ScriptEvaluationResult
> >
> > This CL introduces ScriptEvaluationResult with clearer uniform
> > semantics that covers both classic and module script evaluation.
> > ScriptEvaluationResult has ResultType enum class, to explicitly
> > distinguish cases where normal exceptions are thrown (kException)
> > from cases where not (kNotRun and kAborted).
> >
> > This CL merges ClassicEvaluationResult and ModuleEvaluationResult
> > into ScriptEvaluationResult.
> >
> > This maps:
> > - ClassicEvaluationResult::IsEmpty()
> >   => ScriptEvaluationResult::GetResultType() != kSuccess
> > - ModuleEvaluationResult::IsSuccess() / IsException()
> >   => ScriptEvaluationResult::GetResultType() == kSuccess / != kSuccess
> >
> > To keep the invariant of kSuccess <=> non-empty v8::Value,
> > this CL makes ModuleRecord::Evaluate() to return
> > ScriptEvaluationResult with a non-empty value
> > on successful evaluation + top-level await is not enabled,
> > while previously it returned ModuleEvaluationResult::Empty().
> > This value is not used, and thus this change doesn't change
> > the behavior.
> >
> > Bug: 1111134, 1129743
> > Change-Id: I350860171a3c3b63517641dfb9555b8788386438
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2417789
> > Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
> > Reviewed-by: Kentaro Hara <haraken@chromium.org>
> > Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
> > Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
> > Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
> > Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#811244}
>
> TBR=yukishiino@chromium.org,bashi@chromium.org,haraken@chromium.org,hiroshige@chromium.org,kouhei@chromium.org,nhiroki@chromium.org,cbruni@chromium.org,dom@chromium.org
>
> Change-Id: Iaaca1345213b1f3e4fc8ec703257217998ec0826
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 1111134
> Bug: 1129743
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2435918
> Reviewed-by: Gayane Petrosyan <gayane@chromium.org>
> Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#811258}

TBR=yukishiino@chromium.org,bashi@chromium.org,haraken@chromium.org,hiroshige@chromium.org,kouhei@chromium.org,nhiroki@chromium.org,gayane@chromium.org,cbruni@chromium.org,dom@chromium.org

# Not skipping CQ checks because this is a reland.

Bug: 1111134
Bug: 1129743
Bug: 1132893
Change-Id: I494cf7b5c1d55b65d9bd4552f1ed2d9b05ffae1b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438073Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarHiroshige Hayashizaki <hiroshige@chromium.org>
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811690}
parent 31a07373
......@@ -19,7 +19,6 @@ bindings_core_v8_files =
"core/v8/callback_invoke_helper.cc",
"core/v8/callback_invoke_helper.h",
"core/v8/callback_promise_adapter.h",
"core/v8/classic_evaluation_result.h",
"core/v8/custom/v8_custom_xpath_ns_resolver.cc",
"core/v8/custom/v8_custom_xpath_ns_resolver.h",
"core/v8/custom/v8_dev_tools_host_custom.cc",
......@@ -78,6 +77,8 @@ bindings_core_v8_files =
"core/v8/script_custom_element_definition_builder.cc",
"core/v8/script_custom_element_definition_builder.h",
"core/v8/script_custom_element_definition_data.h",
"core/v8/script_evaluation_result.cc",
"core/v8/script_evaluation_result.h",
"core/v8/script_event_listener.cc",
"core/v8/script_event_listener.h",
"core/v8/script_function.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_CLASSIC_EVALUATION_RESULT_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_CLASSIC_EVALUATION_RESULT_H_
#include "third_party/blink/renderer/bindings/core/v8/script_source_location_type.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/text_position.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "v8/include/v8.h"
namespace blink {
// ClassicEvaluationResult encapsulates the result of a classic script
// evaluation.
// - If IsEmpty() is false:
// A script is evaluated successfully.
// No exceptions are thrown.
// GetValue() returns a non-Empty value.
// - If IsEmpty() is true:
// Script evaluation failed (compile error, evaluation error, or so).
// An exception might or might not be thrown in V8.
// Unlike v8::MaybeLocal<>, there are cases where no exceptions are thrown
// to V8 while returning an Empty ClassicEvaluationResult, like when:
// - An exception is thrown during script evaluation but caught and passed
// to https://html.spec.whatwg.org/C/#report-the-error, instead of
// being rethrown, or
// - Script evaluation is skipped due to checks within Blink.
//
// TODO(crbug/1111134): Consider merging with ModuleEvaluationResult later.
// Right now classic and module evaluation paths are not yet merged, and
// top-level await (crbug/1022182) will modify ModuleEvaluationResult.
class CORE_EXPORT ClassicEvaluationResult final {
STACK_ALLOCATED();
public:
ClassicEvaluationResult() = default;
explicit ClassicEvaluationResult(v8::Local<v8::Value> value) : value_(value) {
DCHECK(!IsEmpty());
}
bool IsEmpty() const { return value_.IsEmpty(); }
v8::Local<v8::Value> GetValue() const {
DCHECK(!IsEmpty());
return value_;
}
private:
v8::Local<v8::Value> value_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_CLASSIC_EVALUATION_RESULT_H_
......@@ -20,51 +20,6 @@
namespace blink {
// static
ModuleEvaluationResult ModuleEvaluationResult::Empty() {
return ModuleEvaluationResult(true, {});
}
// static
ModuleEvaluationResult ModuleEvaluationResult::FromResult(
v8::Local<v8::Value> promise) {
DCHECK(base::FeatureList::IsEnabled(features::kTopLevelAwait) ||
promise.IsEmpty());
DCHECK(!base::FeatureList::IsEnabled(features::kTopLevelAwait) ||
promise->IsPromise());
return ModuleEvaluationResult(true, promise);
}
// static
ModuleEvaluationResult ModuleEvaluationResult::FromException(
v8::Local<v8::Value> exception) {
DCHECK(!exception.IsEmpty());
return ModuleEvaluationResult(false, exception);
}
ModuleEvaluationResult& ModuleEvaluationResult::Escape(
ScriptState::EscapableScope* scope) {
value_ = scope->Escape(value_);
return *this;
}
v8::Local<v8::Value> ModuleEvaluationResult::GetException() const {
DCHECK(IsException());
DCHECK(!value_.IsEmpty());
return value_;
}
ScriptPromise ModuleEvaluationResult::GetPromise(
ScriptState* script_state) const {
DCHECK(base::FeatureList::IsEnabled(features::kTopLevelAwait));
DCHECK(!value_.IsEmpty());
if (IsSuccess()) {
return ScriptPromise(script_state, value_);
} else {
return ScriptPromise::Reject(script_state, value_);
}
}
ModuleRecordProduceCacheData::ModuleRecordProduceCacheData(
v8::Isolate* isolate,
SingleCachedMetadataHandler* cache_handler,
......@@ -168,7 +123,7 @@ ScriptValue ModuleRecord::Instantiate(ScriptState* script_state,
return ScriptValue();
}
ModuleEvaluationResult ModuleRecord::Evaluate(ScriptState* script_state,
ScriptEvaluationResult ModuleRecord::Evaluate(ScriptState* script_state,
v8::Local<v8::Module> record,
const KURL& source_url) {
v8::Isolate* isolate = script_state->GetIsolate();
......@@ -192,13 +147,9 @@ ModuleEvaluationResult ModuleRecord::Evaluate(ScriptState* script_state,
if (!V8ScriptRunner::EvaluateModule(isolate, execution_context, record,
script_state->GetContext())
.ToLocal(&result)) {
return ModuleEvaluationResult::FromException(try_catch.Exception());
}
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
return ModuleEvaluationResult::FromResult(result);
} else {
return ModuleEvaluationResult::Empty();
return ScriptEvaluationResult::FromModuleException(try_catch.Exception());
}
return ScriptEvaluationResult::FromModuleSuccess(result);
}
void ModuleRecord::ReportException(ScriptState* script_state,
......
......@@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_MODULE_RECORD_H_
#include "third_party/blink/renderer/bindings/core/v8/module_request.h"
#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "third_party/blink/renderer/bindings/core/v8/script_source_location_type.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
#include "third_party/blink/renderer/core/core_export.h"
......@@ -24,48 +25,6 @@ class KURL;
class ScriptFetchOptions;
class ScriptState;
class ScriptValue;
class ScriptPromise;
// ModuleEvaluationResult encapsulates the result of a module evaluation.
// - Without top-level-await
// - succeed and not return a value, or
// (IsSuccess() == true), no return value is available.
// - throw any object.
// (IsException() == true && GetException()) returns the thrown exception
// - With top-level-await a module can either
// - return a promise, or
// (IsSuccess() == true && GetPromise()) returns a valid ScriptPromise())
// - throw any object.
// (IsException() == true && GetException()) returns the thrown exception
class CORE_EXPORT ModuleEvaluationResult final {
STACK_ALLOCATED();
public:
ModuleEvaluationResult() = delete;
static ModuleEvaluationResult Empty();
static ModuleEvaluationResult FromResult(v8::Local<v8::Value> promise);
static ModuleEvaluationResult FromException(v8::Local<v8::Value> exception);
ModuleEvaluationResult(const ModuleEvaluationResult& value) = default;
ModuleEvaluationResult& operator=(const ModuleEvaluationResult& value) =
default;
~ModuleEvaluationResult() = default;
ModuleEvaluationResult& Escape(ScriptState::EscapableScope* scope);
bool IsSuccess() const { return is_success_; }
bool IsException() const { return !is_success_; }
v8::Local<v8::Value> GetException() const;
ScriptPromise GetPromise(ScriptState* script_state) const;
private:
ModuleEvaluationResult(bool is_success, v8::Local<v8::Value> value)
: is_success_(is_success), value_(value) {}
bool is_success_;
v8::Local<v8::Value> value_;
};
// ModuleRecordProduceCacheData is a parameter object for
// ModuleRecord::ProduceCache().
......@@ -121,7 +80,7 @@ class CORE_EXPORT ModuleRecord final {
v8::Local<v8::Module> record,
const KURL& source_url);
static ModuleEvaluationResult Evaluate(ScriptState*,
static ScriptEvaluationResult Evaluate(ScriptState*,
v8::Local<v8::Module> record,
const KURL& source_url);
......
......@@ -238,7 +238,7 @@ TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
ASSERT_TRUE(ModuleRecord::Instantiate(scope.GetScriptState(), module_failure,
js_url_f)
.IsEmpty());
ModuleEvaluationResult evaluation_result1 =
ScriptEvaluationResult evaluation_result1 =
ModuleRecord::Evaluate(scope.GetScriptState(), module_failure, js_url_f);
resolver->PrepareMockResolveResult(module_failure);
......@@ -252,12 +252,14 @@ TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
ASSERT_TRUE(
ModuleRecord::Instantiate(scope.GetScriptState(), module, js_url_c)
.IsEmpty());
ModuleEvaluationResult evaluation_result2 =
ScriptEvaluationResult evaluation_result2 =
ModuleRecord::Evaluate(scope.GetScriptState(), module, js_url_f);
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
EXPECT_TRUE(evaluation_result1.IsSuccess());
EXPECT_TRUE(evaluation_result2.IsSuccess());
EXPECT_EQ(evaluation_result1.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
EXPECT_EQ(evaluation_result2.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptValue value1;
ScriptValue value2;
......@@ -273,10 +275,12 @@ TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
EXPECT_FALSE(value2.IsEmpty());
EXPECT_EQ(value1, value2);
} else {
EXPECT_TRUE(evaluation_result1.IsException());
EXPECT_TRUE(evaluation_result2.IsException());
EXPECT_EQ(evaluation_result1.GetException(),
evaluation_result2.GetException());
EXPECT_EQ(evaluation_result1.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_EQ(evaluation_result2.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_EQ(evaluation_result1.GetExceptionForModule(),
evaluation_result2.GetExceptionForModule());
}
ASSERT_EQ(1u, resolver->ResolveCount());
......@@ -300,8 +304,9 @@ TEST_P(ModuleRecordTest, Evaluate) {
ModuleRecord::Instantiate(scope.GetScriptState(), module, js_url);
ASSERT_TRUE(exception.IsEmpty());
EXPECT_TRUE(ModuleRecord::Evaluate(scope.GetScriptState(), module, js_url)
.IsSuccess());
EXPECT_EQ(ModuleRecord::Evaluate(scope.GetScriptState(), module, js_url)
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
v8::Local<v8::Value> value =
ClassicScript::CreateUnspecifiedScript(ScriptSourceCode("window.foo"))
->RunScriptAndReturnValue(&scope.GetFrame());
......@@ -334,12 +339,13 @@ TEST_P(ModuleRecordTest, EvaluateCaptureError) {
ModuleRecord::Instantiate(scope.GetScriptState(), module, js_url);
ASSERT_TRUE(exception.IsEmpty());
ModuleEvaluationResult result =
ScriptEvaluationResult result =
ModuleRecord::Evaluate(scope.GetScriptState(), module, js_url);
v8::Local<v8::Value> value;
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
ASSERT_TRUE(result.IsSuccess());
ASSERT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptValue script_value;
result.GetPromise(scope.GetScriptState())
.Then(v8::Local<v8::Function>(),
......@@ -350,8 +356,9 @@ TEST_P(ModuleRecordTest, EvaluateCaptureError) {
EXPECT_FALSE(script_value.IsEmpty());
value = script_value.V8Value();
} else {
ASSERT_TRUE(result.IsException());
value = result.GetException();
ASSERT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
value = result.GetExceptionForModule();
}
ASSERT_TRUE(value->IsString());
EXPECT_EQ("bar", ToCoreString(v8::Local<v8::String>::Cast(value)));
......
// Copyright 2020 The Chromium 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 "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "base/feature_list.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
ScriptEvaluationResult::ScriptEvaluationResult(
mojom::blink::ScriptType script_type,
ResultType result_type,
v8::Local<v8::Value> value)
:
#if DCHECK_IS_ON()
script_type_(script_type),
#endif
result_type_(result_type),
value_(value) {
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromClassicNotRun() {
return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
ResultType::kNotRun, {});
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromModuleNotRun() {
return ScriptEvaluationResult(mojom::blink::ScriptType::kModule,
ResultType::kNotRun, {});
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromClassicSuccess(
v8::Local<v8::Value> value) {
DCHECK(!value.IsEmpty());
return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
ResultType::kSuccess, value);
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromModuleSuccess(
v8::Local<v8::Value> value) {
DCHECK(!value.IsEmpty());
DCHECK(!base::FeatureList::IsEnabled(features::kTopLevelAwait) ||
value->IsPromise());
return ScriptEvaluationResult(mojom::blink::ScriptType::kModule,
ResultType::kSuccess, value);
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromClassicException() {
return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
ResultType::kException, {});
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromModuleException(
v8::Local<v8::Value> exception) {
DCHECK(!exception.IsEmpty());
return ScriptEvaluationResult(mojom::blink::ScriptType::kModule,
ResultType::kException, exception);
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromClassicAborted() {
return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
ResultType::kAborted, {});
}
// static
ScriptEvaluationResult ScriptEvaluationResult::FromModuleAborted() {
return ScriptEvaluationResult(mojom::blink::ScriptType::kModule,
ResultType::kAborted, {});
}
ScriptEvaluationResult& ScriptEvaluationResult::Escape(
ScriptState::EscapableScope* scope) {
value_ = scope->Escape(value_);
return *this;
}
v8::Local<v8::Value> ScriptEvaluationResult::GetSuccessValue() const {
DCHECK_EQ(result_type_, ResultType::kSuccess);
DCHECK(!value_.IsEmpty());
return value_;
}
v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForModule() const {
#if DCHECK_IS_ON()
DCHECK_EQ(script_type_, mojom::blink::ScriptType::kModule);
#endif
DCHECK_EQ(result_type_, ResultType::kException);
DCHECK(!value_.IsEmpty());
return value_;
}
ScriptPromise ScriptEvaluationResult::GetPromise(
ScriptState* script_state) const {
DCHECK(base::FeatureList::IsEnabled(features::kTopLevelAwait));
#if DCHECK_IS_ON()
DCHECK_EQ(script_type_, mojom::blink::ScriptType::kModule);
#endif
switch (result_type_) {
case ResultType::kSuccess:
return ScriptPromise(script_state, GetSuccessValue());
case ResultType::kException:
return ScriptPromise::Reject(script_state, GetExceptionForModule());
case ResultType::kNotRun:
case ResultType::kAborted:
NOTREACHED();
return ScriptPromise::Reject(script_state, v8::Local<v8::Value>());
}
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_EVALUATION_RESULT_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_EVALUATION_RESULT_H_
#include "third_party/blink/public/mojom/script/script_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "v8/include/v8.h"
namespace blink {
class ScriptPromise;
// ScriptEvaluationResult encapsulates the result of a classic or module script
// evaluation:
// - https://html.spec.whatwg.org/C/#run-a-classic-script
// - https://html.spec.whatwg.org/C/#run-a-module-script
//
// Note: Top-level await (TLA, https://github.com/whatwg/html/pull/4352) will
// affect the semantics where mentioned below.
class CORE_EXPORT ScriptEvaluationResult final {
STACK_ALLOCATED();
public:
ScriptEvaluationResult() = delete;
ScriptEvaluationResult(const ScriptEvaluationResult& value) = default;
ScriptEvaluationResult& operator=(const ScriptEvaluationResult& value) =
default;
~ScriptEvaluationResult() = default;
enum class ResultType {
// The script is not evaluated.
// Spec: NormalCompletion with empty [[Value]]
// |value_| is empty.
kNotRun,
// The script is successfully evaluated.
// Spec: #run-a-classic-script/#run-a-module-script return
// NormalCompletion with non-empty [[Value]].
// |value_| is its non-empty [[Value]].
//
// Modules after TLA:
// |value_| is the promise returned by #run-a-module-script.
// Note: The promise can be rejected.
// The script is either:
// - Successfully evaluated synchronously
// (|value_|'s [[PromiseState]] is fulfilled), or
// - Throwing synchronously during evaluation
// (|value_|'s [[PromiseState]] is rejected), or
// - Successfully evaluated until a top-level await and is waiting for the
// promise awaited
// (|value_|'s [[PromiseState]] is pending).
kSuccess,
// The script is evaluated and an exception is thrown.
// Spec: #run-a-classic-script/#run-a-module-script return an abrupt
// completion.
// |value_| is the non-empty exception thrown for module scripts, or
// empty for classic scripts.
//
// Note: The exception can be already caught and passed to
// https://html.spec.whatwg.org/C/#report-the-error, instead of being
// rethrown.
//
// Modules after TLA: #run-a-module-script returns a promise where
// [[PromiseState]] is rejected and [[PromiseResult]] is |value_|,
// only if #concept-script-error-to-rethrow is not null, i.e.
// only for parse/instantiation errors.
// Module scripts throwing synchronously during evaluation return kSuccess.
// TODO(cbruni, hiroshige): Consider cleaning up this semantics.
kException,
// The script is evaluated and aborted prematurely by
// #abort-a-running-script.
// |value_| is empty.
// This corresponds to that TryCatch::CanContinue() is false, and currently
// only checked in classic scripts in WorkerOrWorkletGlobalScope, for
// handling e.g.
// worker.terminate().
// TODO(crbug.com/1129793): Check TryCatch::CanContinue() also in module
// scripts.
kAborted
};
static ScriptEvaluationResult FromClassicNotRun();
static ScriptEvaluationResult FromClassicSuccess(v8::Local<v8::Value> value);
static ScriptEvaluationResult FromClassicException();
static ScriptEvaluationResult FromClassicAborted();
static ScriptEvaluationResult FromModuleNotRun();
static ScriptEvaluationResult FromModuleSuccess(v8::Local<v8::Value> value);
static ScriptEvaluationResult FromModuleException(
v8::Local<v8::Value> exception);
static ScriptEvaluationResult FromModuleAborted();
ScriptEvaluationResult& Escape(ScriptState::EscapableScope* scope);
ResultType GetResultType() const { return result_type_; }
// Can be called only when GetResultType() == kSuccess.
// TODO(crbug.com/1132793): Fix some of the callers (in unit tests) that
// expect bool, string, etc., because after TLA is enabled this will return a
// promise for modules.
v8::Local<v8::Value> GetSuccessValue() const;
// Returns the exception thrown.
// Can be called only when GetResultType() == kException.
v8::Local<v8::Value> GetExceptionForModule() const;
// Returns the promise returned by #run-a-module-script.
// Can be called only
// - For module script with TLA is enabled, and
// - If GetResultType() == kSuccess or kException.
// (For kNotRun/kAborted, we should do nothing)
ScriptPromise GetPromise(ScriptState* script_state) const;
private:
ScriptEvaluationResult(mojom::blink::ScriptType,
ResultType,
v8::Local<v8::Value>);
#if DCHECK_IS_ON()
mojom::blink::ScriptType script_type_;
#endif
ResultType result_type_;
v8::Local<v8::Value> value_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_EVALUATION_RESULT_H_
......@@ -312,13 +312,14 @@ void WorkerOrWorkletScriptController::DisableEvalInternal(
}
// https://html.spec.whatwg.org/C/#run-a-classic-script
ClassicEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
ScriptEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
const ScriptSourceCode& source_code,
SanitizeScriptErrors sanitize_script_errors,
mojom::blink::V8CacheOptions v8_cache_options,
RethrowErrorsOption rethrow_errors) {
if (IsExecutionForbidden())
return ClassicEvaluationResult();
if (IsExecutionForbidden()) {
return ScriptEvaluationResult::FromClassicNotRun();
}
// Scope for |TRACE_EVENT1| and |v8::TryCatch| below.
{
......@@ -368,7 +369,7 @@ ClassicEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
// script evaluation code paths.
if (!block.CanContinue()) {
ForbidExecution();
return ClassicEvaluationResult();
return ScriptEvaluationResult::FromClassicAborted();
}
CHECK(!IsExecutionForbidden());
......@@ -377,10 +378,9 @@ ClassicEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
// Step 10. If evaluationStatus is a normal completion, then return
// evaluationStatus. [spec text]
v8::Local<v8::Value> result;
if (!maybe_result.ToLocal(&result))
return ClassicEvaluationResult();
return ClassicEvaluationResult(result);
bool success = maybe_result.ToLocal(&result);
DCHECK(success);
return ScriptEvaluationResult::FromClassicSuccess(result);
}
DCHECK(maybe_result.IsEmpty());
......@@ -398,7 +398,7 @@ ClassicEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
// reported to WorkerGlobalScope.onerror via `TryCatch::SetVerbose(true)`
// called at top-level worker script evaluation.
block.ReThrow();
return ClassicEvaluationResult();
return ScriptEvaluationResult::FromClassicException();
}
}
// |v8::TryCatch| is (and should be) exited, before ThrowException() below.
......@@ -418,12 +418,12 @@ ClassicEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
isolate_, V8ThrowDOMException::CreateOrEmpty(
isolate_, DOMExceptionCode::kNetworkError,
rethrow_errors.Message()));
return ClassicEvaluationResult();
return ScriptEvaluationResult::FromClassicException();
}
// #report-the-error for rethrow errors == true is already handled via
// |TryCatch::SetVerbose(true)| above.
return ClassicEvaluationResult();
return ScriptEvaluationResult::FromClassicException();
}
void WorkerOrWorkletScriptController::ForbidExecution() {
......
......@@ -33,9 +33,9 @@
#include "base/macros.h"
#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/classic_evaluation_result.h"
#include "third_party/blink/renderer/bindings/core/v8/rejected_promises.h"
#include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/core_export.h"
......@@ -96,7 +96,7 @@ class CORE_EXPORT WorkerOrWorkletScriptController final
// https://html.spec.whatwg.org/C/#run-a-classic-script
// Callers should enter ScriptState::Scope before calling this.
ClassicEvaluationResult EvaluateAndReturnValue(
ScriptEvaluationResult EvaluateAndReturnValue(
const ScriptSourceCode&,
SanitizeScriptErrors sanitize_script_errors,
mojom::blink::V8CacheOptions = mojom::blink::V8CacheOptions::kDefault,
......
......@@ -44,7 +44,7 @@ class LayoutWorkletTest : public PageTestBase {
return GetGlobalScope()->ScriptController()->GetScriptState();
}
ModuleEvaluationResult EvaluateScriptModule(const String& source_code) {
ScriptEvaluationResult EvaluateScriptModule(const String& source_code) {
ScriptState* script_state = GetScriptState();
EXPECT_TRUE(script_state);
......@@ -69,7 +69,7 @@ class LayoutWorkletTest : public PageTestBase {
TEST_F(LayoutWorkletTest, ParseProperties) {
ScriptState::Scope scope(GetScriptState());
EXPECT_TRUE(EvaluateScriptModule(R"JS(
EXPECT_EQ(EvaluateScriptModule(R"JS(
registerLayout('foo', class {
static get inputProperties() { return ['--prop', 'flex-basis', 'thing'] }
static get childInputProperties() { return ['--child-prop', 'margin-top', 'other-thing'] }
......@@ -77,7 +77,8 @@ TEST_F(LayoutWorkletTest, ParseProperties) {
async layout() { }
});
)JS")
.IsSuccess());
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
LayoutWorkletGlobalScope* global_scope = GetGlobalScope();
CSSLayoutDefinition* definition = global_scope->FindDefinition("foo");
......@@ -105,14 +106,15 @@ TEST_F(LayoutWorkletTest, ParseProperties) {
TEST_F(LayoutWorkletTest, RegisterLayout) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
async intrinsicSizes() { }
async layout() { }
});
)JS");
EXPECT_TRUE(result.IsSuccess());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
result = EvaluateScriptModule(R"JS(
registerLayout('bar', class {
......@@ -123,23 +125,25 @@ TEST_F(LayoutWorkletTest, RegisterLayout) {
});
)JS");
EXPECT_TRUE(result.IsSuccess());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
}
TEST_F(LayoutWorkletTest, RegisterLayout_EmptyName) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('', class {
});
)JS");
// "The empty string is not a valid name."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_Duplicate) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
async intrinsicSizes() { }
async layout() { }
......@@ -151,95 +155,103 @@ TEST_F(LayoutWorkletTest, RegisterLayout_Duplicate) {
)JS");
// "A class with name:'foo' is already registered."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoIntrinsicSizes) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
});
)JS");
// "The 'intrinsicSizes' property on the prototype does not exist."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_ThrowingPropertyGetter) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
static get inputProperties() { throw Error(); }
});
)JS");
// "Uncaught Error"
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadPropertyGetter) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
static get inputProperties() { return 42; }
});
)JS");
// "The provided value cannot be converted to a sequence."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoPrototype) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
const foo = function() { };
foo.prototype = undefined;
registerLayout('foo', foo);
)JS");
// "The 'prototype' object on the class does not exist."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadPrototype) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
const foo = function() { };
foo.prototype = 42;
registerLayout('foo', foo);
)JS");
// "The 'prototype' property on the class is not an object."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadIntrinsicSizes) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
get intrinsicSizes() { return 42; }
});
)JS");
// "The 'intrinsicSizes' property on the prototype is not a function."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoLayout) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
async intrinsicSizes() { }
});
)JS");
// "The 'layout' property on the prototype does not exist."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadLayout) {
ScriptState::Scope scope(GetScriptState());
ModuleEvaluationResult result = EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
async intrinsicSizes() { }
get layout() { return 42; }
......@@ -247,7 +259,8 @@ TEST_F(LayoutWorkletTest, RegisterLayout_BadLayout) {
)JS");
// "The 'layout' property on the prototype is not a function."
EXPECT_TRUE(result.IsException());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
}
} // namespace blink
......@@ -57,11 +57,11 @@ bool ClassicScript::RunScriptOnWorkerOrWorklet(
DCHECK(global_scope.IsContextThread());
ScriptState::Scope scope(global_scope.ScriptController()->GetScriptState());
ClassicEvaluationResult result =
ScriptEvaluationResult result =
global_scope.ScriptController()->EvaluateAndReturnValue(
GetScriptSourceCode(), sanitize_script_errors_,
global_scope.GetV8CacheOptions());
return !result.IsEmpty();
return result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess;
}
std::pair<size_t, size_t> ClassicScript::GetClassicScriptSizes() const {
......
......@@ -153,85 +153,94 @@ void DynamicImportTreeClient::NotifyModuleTreeLoadFinished(
// <spec step="7">Run the module script result, with the rethrow errors
// boolean set to true.</spec>
ModuleEvaluationResult result = modulator_->ExecuteModule(
ScriptEvaluationResult result = modulator_->ExecuteModule(
module_script, Modulator::CaptureEvalErrorFlag::kCapture);
// <spec step="8">If running the module script throws an exception, ...</spec>
if (result.IsException()) {
// <spec step="8">... then perform
// FinishDynamicImport(referencingScriptOrModule, specifier,
// promiseCapability, the thrown exception completion).</spec>
//
// Note: "the thrown exception completion" is |error|.
//
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="1">If completion is an abrupt completion, then perform !
// Call(promiseCapability.[[Reject]], undefined, « completion.[[Value]]
// »).</spec>
promise_resolver_->Reject(result.GetException());
return;
}
// <spec step="9">Otherwise, perform
// FinishDynamicImport(referencingScriptOrModule, specifier,
// promiseCapability, NormalCompletion(undefined)).</spec>
//
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.1">Assert: completion is a normal completion and
// completion.[[Value]] is undefined.</spec>
DCHECK(result.IsSuccess());
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
ScriptPromise promise = result.GetPromise(script_state);
v8::Local<v8::Function> callback_success =
ModuleResolutionSuccessCallback::CreateFunction(
script_state, promise_resolver_, module_script);
v8::Local<v8::Function> callback_failure =
ModuleResolutionFailureCallback::CreateFunction(script_state,
promise_resolver_);
promise.Then(callback_success, callback_failure);
return;
}
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.2">Let moduleRecord be !
// HostResolveImportedModule(referencingScriptOrModule, specifier).</spec>
//
// Note: We skip invocation of ModuleRecordResolver here. The
// result of HostResolveImportedModule is guaranteed to be |module_script|.
v8::Local<v8::Module> record = module_script->V8Module();
DCHECK(!record.IsEmpty());
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.3">Assert: Evaluate has already been invoked on moduleRecord and
// successfully completed.</spec>
//
// Because |error| is empty, we are sure that ExecuteModule() above was
// successfully completed.
switch (result.GetResultType()) {
case ScriptEvaluationResult::ResultType::kException:
// <spec step="8">If running the module script throws an exception,
// ...</spec> <spec step="8">... then perform
// FinishDynamicImport(referencingScriptOrModule, specifier,
// promiseCapability, the thrown exception completion).</spec>
//
// Note: "the thrown exception completion" is |error|.
//
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="1">If completion is an abrupt completion, then perform !
// Call(promiseCapability.[[Reject]], undefined, « completion.[[Value]]
// »).</spec>
promise_resolver_->Reject(result.GetExceptionForModule());
break;
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.4">Let namespace be GetModuleNamespace(moduleRecord).</spec>
v8::Local<v8::Value> module_namespace = ModuleRecord::V8Namespace(record);
case ScriptEvaluationResult::ResultType::kNotRun:
case ScriptEvaluationResult::ResultType::kAborted:
// Do nothing when script is disabled or after a script is aborted.
break;
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.5">If namespace is an abrupt completion, perform !
// Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]]
// »).</spec>
//
// Note: Blink's implementation never allows |module_namespace| to be
// an abrupt completion.
case ScriptEvaluationResult::ResultType::kSuccess: {
// <spec step="9">Otherwise, perform
// FinishDynamicImport(referencingScriptOrModule, specifier,
// promiseCapability, NormalCompletion(undefined)).</spec>
//
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.1">Assert: completion is a normal completion and
// completion.[[Value]] is undefined.</spec>
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
ScriptPromise promise = result.GetPromise(script_state);
v8::Local<v8::Function> callback_success =
ModuleResolutionSuccessCallback::CreateFunction(
script_state, promise_resolver_, module_script);
v8::Local<v8::Function> callback_failure =
ModuleResolutionFailureCallback::CreateFunction(script_state,
promise_resolver_);
promise.Then(callback_success, callback_failure);
return;
}
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.6">Otherwise, perform ! Call(promiseCapability.[[Resolve]],
// undefined, « namespace.[[Value]] »).</spec>
promise_resolver_->Resolve(module_namespace);
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.2">Let moduleRecord be !
// HostResolveImportedModule(referencingScriptOrModule, specifier).</spec>
//
// Note: We skip invocation of ModuleRecordResolver here. The
// result of HostResolveImportedModule is guaranteed to be
// |module_script|.
v8::Local<v8::Module> record = module_script->V8Module();
DCHECK(!record.IsEmpty());
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.3">Assert: Evaluate has already been invoked on moduleRecord
// and successfully completed.</spec>
//
// Because |error| is empty, we are sure that ExecuteModule() above was
// successfully completed.
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.4">Let namespace be GetModuleNamespace(moduleRecord).</spec>
v8::Local<v8::Value> module_namespace = ModuleRecord::V8Namespace(record);
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.5">If namespace is an abrupt completion, perform !
// Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]]
// »).</spec>
//
// Note: Blink's implementation never allows |module_namespace| to be
// an abrupt completion.
// <spec
// href="https://tc39.github.io/proposal-dynamic-import/#sec-finishdynamicimport"
// step="2.6">Otherwise, perform ! Call(promiseCapability.[[Resolve]],
// undefined, « namespace.[[Value]] »).</spec>
promise_resolver_->Resolve(module_namespace);
break;
}
}
}
void DynamicImportTreeClient::Trace(Visitor* visitor) const {
......
......@@ -91,13 +91,13 @@ class DynamicModuleResolverTestModulator final : public DummyModulator {
fetch_tree_was_called_ = true;
}
ModuleEvaluationResult ExecuteModule(
ScriptEvaluationResult ExecuteModule(
ModuleScript* module_script,
CaptureEvalErrorFlag capture_error) final {
EXPECT_EQ(CaptureEvalErrorFlag::kCapture, capture_error);
ScriptState::EscapableScope scope(script_state_);
ModuleEvaluationResult result = ModuleRecord::Evaluate(
ScriptEvaluationResult result = ModuleRecord::Evaluate(
script_state_, module_script->V8Module(), module_script->SourceURL());
return result.Escape(&scope);
}
......
......@@ -201,14 +201,14 @@ class CORE_EXPORT Modulator : public GarbageCollected<Modulator>,
// CaptureEvalErrorFlag is used to implement "rethrow errors" parameter in
// run-a-module-script.
// - When "rethrow errors" is to be set, use kCapture for EvaluateModule().
// Then EvaluateModule() wraps exceptions in a ModuleEvaluationResult instead
// Then EvaluateModule() wraps exceptions in a ScriptEvaluationResult instead
// of throwing it and the caller should rethrow the exception.
// - When "rethrow errors" is not to be set, use kReport. If there is an error
// to throw, EvaluateModule() "report the error" inside it, and returns
// ModuleEvaluationResult wrapping the error. Otherwise, it returns either a
// ModuleEvaluationResult that is empty or contains the successful evaluation
// ScriptEvaluationResult wrapping the error. Otherwise, it returns either a
// ScriptEvaluationResult that is empty or contains the successful evaluation
// result.
virtual ModuleEvaluationResult ExecuteModule(ModuleScript*,
virtual ScriptEvaluationResult ExecuteModule(ModuleScript*,
CaptureEvalErrorFlag) = 0;
virtual ModuleScriptFetcher* CreateModuleScriptFetcher(
......
......@@ -337,7 +337,7 @@ void ModulatorImplBase::ProduceCacheModuleTree(
// <specdef href="https://html.spec.whatwg.org/C/#run-a-module-script">
// Spec with TLA: https://github.com/whatwg/html/pull/4352
ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
ScriptEvaluationResult ModulatorImplBase::ExecuteModule(
ModuleScript* module_script,
CaptureEvalErrorFlag capture_error) {
// <spec step="1">If rethrow errors is not given, let it be false.</spec>
......@@ -348,8 +348,9 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
// <spec step="3">Check if we can run script with settings. If this returns
// "do not run" then return NormalCompletion(empty).</spec>
if (IsScriptingDisabled())
return ModuleEvaluationResult::Empty();
if (IsScriptingDisabled()) {
return ScriptEvaluationResult::FromModuleNotRun();
}
// <spec step="4">Prepare to run script given settings.</spec>
//
......@@ -361,7 +362,7 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
ScriptState::EscapableScope scope(script_state_);
// Without TLA: <spec step="5">Let evaluationStatus be null.</spec>
ModuleEvaluationResult result = ModuleEvaluationResult::Empty();
ScriptEvaluationResult result = ScriptEvaluationResult::FromModuleNotRun();
// <spec step="6">If script's error to rethrow is not null, ...</spec>
if (module_script->HasErrorToRethrow()) {
......@@ -371,7 +372,7 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
// With TLA: <spec step="5">If script's error to rethrow is not null,
// then let valuationPromise be a promise rejected with script's error
// to rethrow.</spec>
result = ModuleEvaluationResult::FromException(
result = ScriptEvaluationResult::FromModuleException(
module_script->CreateErrorToRethrow().V8Value());
} else {
// <spec step="7">Otherwise:</spec>
......@@ -390,7 +391,8 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
// DOMException, [[Target]]: empty }.</spec>
// [not specced] Store V8 code cache on successful evaluation.
if (result.IsSuccess()) {
if (result.GetResultType() ==
ScriptEvaluationResult::ResultType::kSuccess) {
TaskRunner()->PostTask(
FROM_HERE,
WTF::Bind(&ModulatorImplBase::ProduceCacheModuleTreeTopLevel,
......@@ -398,27 +400,26 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
}
}
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
if (capture_error == CaptureEvalErrorFlag::kReport) {
if (capture_error == CaptureEvalErrorFlag::kReport) {
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
// <spec step="7"> If report errors is true, then upon rejection of
// evaluationPromise with reason, report the exception given by reason
// for script.</spec>
v8::Local<v8::Function> callback_failure =
ModuleEvaluationRejectionCallback::CreateFunction(script_state_);
// Add a rejection handler to report back errors once the result promise
// is rejected.
// Add a rejection handler to report back errors once the result
// promise is rejected.
result.GetPromise(script_state_)
.Then(v8::Local<v8::Function>(), callback_failure);
}
} else {
// <spec step="8">If evaluationStatus is an abrupt completion, then:</spec>
if (result.IsException()) {
// <spec step="8.1">If rethrow errors is true, rethrow the exception given
// by evaluationStatus.[[Value]].</spec>
if (capture_error == CaptureEvalErrorFlag::kReport) {
} else {
// <spec step="8">If evaluationStatus is an abrupt completion,
// then:</spec>
if (result.GetResultType() ==
ScriptEvaluationResult::ResultType::kException) {
// <spec step="8.2">Otherwise, report the exception given by
// evaluationStatus.[[Value]] for script.</spec>
ModuleRecord::ReportException(script_state_, result.GetException());
ModuleRecord::ReportException(script_state_,
result.GetExceptionForModule());
}
}
}
......
......@@ -89,7 +89,7 @@ class ModulatorImplBase : public Modulator {
ScriptValue InstantiateModule(v8::Local<v8::Module>, const KURL&) override;
Vector<ModuleRequest> ModuleRequestsFromModuleRecord(
v8::Local<v8::Module>) override;
ModuleEvaluationResult ExecuteModule(ModuleScript*,
ScriptEvaluationResult ExecuteModule(ModuleScript*,
CaptureEvalErrorFlag) override;
// Populates |reason| and returns true if the dynamic import is disallowed on
......
......@@ -101,7 +101,7 @@ void ModuleScript::Trace(Visitor* visitor) const {
}
void ModuleScript::RunScript(LocalFrame* frame) {
// We need a HandleScope for the ModuleEvaluationResult that is created
// We need a HandleScope for the ScriptEvaluationResult that is created
// in ::ExecuteModule(...).
ScriptState::Scope scope(SettingsObject()->GetScriptState());
DVLOG(1) << *this << "::RunScript()";
......@@ -112,7 +112,7 @@ void ModuleScript::RunScript(LocalFrame* frame) {
bool ModuleScript::RunScriptOnWorkerOrWorklet(
WorkerOrWorkletGlobalScope& global_scope) {
// We need a HandleScope for the ModuleEvaluationResult that is created
// We need a HandleScope for the ScriptEvaluationResult that is created
// in ::ExecuteModule(...).
ScriptState::Scope scope(SettingsObject()->GetScriptState());
DCHECK(global_scope.IsContextThread());
......@@ -120,9 +120,10 @@ bool ModuleScript::RunScriptOnWorkerOrWorklet(
// This |error| is always null because the second argument is |kReport|.
// TODO(nhiroki): Catch an error when an evaluation error happens.
// (https://crbug.com/680046)
ModuleEvaluationResult result = SettingsObject()->ExecuteModule(
ScriptEvaluationResult result = SettingsObject()->ExecuteModule(
this, Modulator::CaptureEvalErrorFlag::kReport);
return result.IsSuccess();
return result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess;
}
std::pair<size_t, size_t> ModuleScript::GetClassicScriptSizes() const {
......
......@@ -167,10 +167,11 @@ TEST_P(ModuleScriptTest, V8CodeCacheWithoutDiscarding) {
module_script->V8Module(),
module_script->SourceURL())
.IsEmpty());
ASSERT_TRUE(ModuleRecord::Evaluate(scope.GetScriptState(),
module_script->V8Module(),
module_script->SourceURL())
.IsSuccess());
ASSERT_EQ(ModuleRecord::Evaluate(scope.GetScriptState(),
module_script->V8Module(),
module_script->SourceURL())
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
TestFoo(scope);
Checkpoint checkpoint;
......@@ -293,10 +294,11 @@ TEST_P(ModuleScriptTest, V8CodeCacheWithDiscarding) {
module_script->V8Module(),
module_script->SourceURL())
.IsEmpty());
ASSERT_TRUE(ModuleRecord::Evaluate(scope.GetScriptState(),
module_script->V8Module(),
module_script->SourceURL())
.IsSuccess());
ASSERT_EQ(ModuleRecord::Evaluate(scope.GetScriptState(),
module_script->V8Module(),
module_script->SourceURL())
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
TestFoo(scope);
Checkpoint checkpoint;
......
......@@ -169,10 +169,10 @@ Vector<ModuleRequest> DummyModulator::ModuleRequestsFromModuleRecord(
return Vector<ModuleRequest>();
}
ModuleEvaluationResult DummyModulator::ExecuteModule(ModuleScript*,
ScriptEvaluationResult DummyModulator::ExecuteModule(ModuleScript*,
CaptureEvalErrorFlag) {
NOTREACHED();
return ModuleEvaluationResult::Empty();
return ScriptEvaluationResult::FromModuleNotRun();
}
ModuleScriptFetcher* DummyModulator::CreateModuleScriptFetcher(
......
......@@ -75,7 +75,7 @@ class DummyModulator : public Modulator {
ScriptValue InstantiateModule(v8::Local<v8::Module>, const KURL&) override;
Vector<ModuleRequest> ModuleRequestsFromModuleRecord(
v8::Local<v8::Module>) override;
ModuleEvaluationResult ExecuteModule(ModuleScript*,
ScriptEvaluationResult ExecuteModule(ModuleScript*,
CaptureEvalErrorFlag) override;
ModuleScriptFetcher* CreateModuleScriptFetcher(
ModuleScriptCustomFetchType,
......
......@@ -330,7 +330,7 @@ void WorkerGlobalScope::ImportScriptsInternal(const Vector<String>& urls) {
ReportingProxy().WillEvaluateImportedClassicScript(
source_code.length(), handler ? handler->GetCodeCacheSize() : 0);
ScriptState::Scope scope(ScriptController()->GetScriptState());
ClassicEvaluationResult result = ScriptController()->EvaluateAndReturnValue(
ScriptEvaluationResult result = ScriptController()->EvaluateAndReturnValue(
ScriptSourceCode(source_code, ScriptSourceLocationType::kUnknown,
handler,
ScriptSourceCode::UsePostRedirectURL() ? response_url
......@@ -342,7 +342,7 @@ void WorkerGlobalScope::ImportScriptsInternal(const Vector<String>& urls) {
// Step 5.2: "If an exception was thrown or if the script was prematurely
// aborted, then abort all these steps, letting the exception or aborting
// continue to be processed by the calling script."
if (result.IsEmpty())
if (result.GetResultType() != ScriptEvaluationResult::ResultType::kSuccess)
return;
}
}
......
......@@ -70,7 +70,7 @@ void WorkletModuleTreeClient::NotifyModuleTreeLoadFinished(
}
// Step 5: "Run a module script given script."
ModuleEvaluationResult result =
ScriptEvaluationResult result =
Modulator::From(script_state_)
->ExecuteModule(module_script,
Modulator::CaptureEvalErrorFlag::kReport);
......@@ -78,7 +78,8 @@ void WorkletModuleTreeClient::NotifyModuleTreeLoadFinished(
auto* global_scope =
To<WorkletGlobalScope>(ExecutionContext::From(script_state_));
global_scope->ReportingProxy().DidEvaluateTopLevelScript(result.IsSuccess());
global_scope->ReportingProxy().DidEvaluateTopLevelScript(
result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess);
// Step 6: "Queue a task on outsideSettings's responsible event loop to run
// these steps:"
......
......@@ -157,11 +157,12 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
DCHECK(isolate);
ScriptState::Scope scope(script_state);
ClassicEvaluationResult result =
ScriptEvaluationResult result =
global_scope->ScriptController()->EvaluateAndReturnValue(
ScriptSourceCode(script), SanitizeScriptErrors::kSanitize);
DCHECK(!result.IsEmpty());
return ToBoolean(isolate, result.GetValue(), ASSERT_NO_EXCEPTION);
DCHECK_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
return ToBoolean(isolate, result.GetSuccessValue(), ASSERT_NO_EXCEPTION);
}
void RunConstructAndAnimateTestOnWorklet(
......
......@@ -148,9 +148,10 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
ModuleRecord::Instantiate(script_state, module, js_url);
EXPECT_TRUE(exception.IsEmpty());
ModuleEvaluationResult result =
ScriptEvaluationResult result =
ModuleRecord::Evaluate(script_state, module, js_url);
return result.IsSuccess();
return result.GetResultType() ==
ScriptEvaluationResult::ResultType::kSuccess;
}
// Test if AudioWorkletGlobalScope and V8 components (ScriptState, Isolate)
......
......@@ -120,9 +120,10 @@ class AudioWorkletThreadTest : public PageTestBase {
ScriptValue exception =
ModuleRecord::Instantiate(script_state, module, js_url);
EXPECT_TRUE(exception.IsEmpty());
ModuleEvaluationResult result =
ScriptEvaluationResult result =
ModuleRecord::Evaluate(script_state, module, js_url);
EXPECT_TRUE(result.IsSuccess());
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
wait_event->Signal();
}
......
......@@ -81,8 +81,9 @@ class AnimationAndPaintWorkletThreadTest : public PageTestBase {
ScriptValue exception =
ModuleRecord::Instantiate(script_state, module, js_url);
EXPECT_TRUE(exception.IsEmpty());
EXPECT_TRUE(
ModuleRecord::Evaluate(script_state, module, js_url).IsSuccess());
EXPECT_EQ(
ModuleRecord::Evaluate(script_state, module, js_url).GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
wait_event->Signal();
}
};
......
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