LibJS: Stop tracking whether execution context is strict mode or not

This was only used for basic testing, and forced us to plumb this flag
flag in a bunch of places.
This commit is contained in:
Andreas Kling 2025-10-28 21:16:35 +01:00 committed by Andreas Kling
parent fb05063dde
commit fdb85a330e
19 changed files with 4 additions and 320 deletions

View File

@ -249,9 +249,6 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
// 8. Set the PrivateEnvironment of scriptContext to null. // 8. Set the PrivateEnvironment of scriptContext to null.
// NOTE: This isn't in the spec, but we require it.
script_context->is_strict_mode = script_record.is_strict_mode();
// 9. Suspend the currently running execution context. // 9. Suspend the currently running execution context.
// 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context. // 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context.
TRY(vm.push_execution_context(*script_context, {})); TRY(vm.push_execution_context(*script_context, {}));

View File

@ -723,9 +723,6 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
// 28. Set evalContext's PrivateEnvironment to privateEnv. // 28. Set evalContext's PrivateEnvironment to privateEnv.
eval_context->private_environment = private_environment; eval_context->private_environment = private_environment;
// NOTE: This isn't in the spec, but we require it.
eval_context->is_strict_mode = strict_eval;
// 29. Push evalContext onto the execution context stack; evalContext is now the running execution context. // 29. Push evalContext onto the execution context stack; evalContext is now the running execution context.
TRY(vm.push_execution_context(*eval_context, {})); TRY(vm.push_execution_context(*eval_context, {}));

View File

@ -689,9 +689,6 @@ void ECMAScriptFunctionObject::make_method(Object& home_object)
// 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall // 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall
void ECMAScriptFunctionObject::prepare_for_ordinary_call(VM& vm, ExecutionContext& callee_context, Object* new_target) void ECMAScriptFunctionObject::prepare_for_ordinary_call(VM& vm, ExecutionContext& callee_context, Object* new_target)
{ {
// Non-standard
callee_context.is_strict_mode = is_strict_mode();
// 1. Let callerContext be the running execution context. // 1. Let callerContext be the running execution context.
// 2. Let calleeContext be a new ECMAScript code execution context. // 2. Let calleeContext be a new ECMAScript code execution context.

View File

@ -125,7 +125,6 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
copy->program_counter = program_counter; copy->program_counter = program_counter;
copy->function_name = function_name; copy->function_name = function_name;
copy->this_value = this_value; copy->this_value = this_value;
copy->is_strict_mode = is_strict_mode;
copy->executable = executable; copy->executable = executable;
copy->arguments_offset = arguments_offset; copy->arguments_offset = arguments_offset;
copy->passed_argument_count = passed_argument_count; copy->passed_argument_count = passed_argument_count;

View File

@ -95,7 +95,6 @@ public:
u32 arguments_offset { 0 }; u32 arguments_offset { 0 };
u32 passed_argument_count { 0 }; u32 passed_argument_count { 0 };
bool is_strict_mode { false };
Span<Value> arguments; Span<Value> arguments;

View File

@ -151,9 +151,6 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(ExecutionContext& callee_
// calling async_block_start which goes through a NativeFunction here. // calling async_block_start which goes through a NativeFunction here.
callee_context.private_environment = caller_context.private_environment; callee_context.private_environment = caller_context.private_environment;
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = caller_context.is_strict_mode;
// </8.> -------------------------------------------------------------------------- // </8.> --------------------------------------------------------------------------
// 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
@ -204,9 +201,6 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ExecutionC
callee_context.lexical_environment = caller_context.lexical_environment; callee_context.lexical_environment = caller_context.lexical_environment;
callee_context.variable_environment = caller_context.variable_environment; callee_context.variable_environment = caller_context.variable_environment;
// NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
callee_context.is_strict_mode = caller_context.is_strict_mode;
// </8.> -------------------------------------------------------------------------- // </8.> --------------------------------------------------------------------------
// 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.

View File

@ -342,9 +342,6 @@ NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bo
// 10. Set context's PrivateEnvironment to null. // 10. Set context's PrivateEnvironment to null.
context->private_environment = nullptr; context->private_environment = nullptr;
// Non-standard
context->is_strict_mode = strict_eval;
// 11. Return context. // 11. Return context.
return context; return context;
} }

View File

@ -25,18 +25,15 @@ Result<GC::Ref<Script>, Vector<ParserError>> Script::parse(StringView source_tex
if (parser.has_errors()) if (parser.has_errors())
return parser.errors(); return parser.errors();
bool strict_mode = script->is_strict_mode();
// 3. Return Script Record { [[Realm]]: realm, [[ECMAScriptCode]]: script, [[HostDefined]]: hostDefined }. // 3. Return Script Record { [[Realm]]: realm, [[ECMAScriptCode]]: script, [[HostDefined]]: hostDefined }.
return realm.heap().allocate<Script>(realm, filename, move(script), host_defined, strict_mode); return realm.heap().allocate<Script>(realm, filename, move(script), host_defined);
} }
Script::Script(Realm& realm, StringView filename, NonnullRefPtr<Program> parse_node, HostDefined* host_defined, bool strict_mode) Script::Script(Realm& realm, StringView filename, NonnullRefPtr<Program> parse_node, HostDefined* host_defined)
: m_realm(realm) : m_realm(realm)
, m_parse_node(move(parse_node)) , m_parse_node(move(parse_node))
, m_filename(filename) , m_filename(filename)
, m_host_defined(host_defined) , m_host_defined(host_defined)
, m_strict_mode(strict_mode)
{ {
} }

View File

@ -46,10 +46,8 @@ public:
HostDefined* host_defined() const { return m_host_defined; } HostDefined* host_defined() const { return m_host_defined; }
StringView filename() const LIFETIME_BOUND { return m_filename; } StringView filename() const LIFETIME_BOUND { return m_filename; }
[[nodiscard]] bool is_strict_mode() const { return m_strict_mode; }
private: private:
Script(Realm&, StringView filename, NonnullRefPtr<Program>, HostDefined*, bool strict_mode); Script(Realm&, StringView filename, NonnullRefPtr<Program>, HostDefined*);
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
@ -60,8 +58,6 @@ private:
// Needed for potential lookups of modules. // Needed for potential lookups of modules.
ByteString m_filename; ByteString m_filename;
HostDefined* m_host_defined { nullptr }; // [[HostDefined]] HostDefined* m_host_defined { nullptr }; // [[HostDefined]]
bool m_strict_mode { false };
}; };
} }

View File

@ -709,9 +709,6 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise
ExecutionContext* module_context = nullptr; ExecutionContext* module_context = nullptr;
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(module_context, registers_and_constants_and_locals_count, 0); ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(module_context, registers_and_constants_and_locals_count, 0);
// NOTE: This is not in the spec but we require it.
module_context->is_strict_mode = true;
// 2. Set the Function of moduleContext to null. // 2. Set the Function of moduleContext to null.
// 3. Set the Realm of moduleContext to module.[[Realm]]. // 3. Set the Realm of moduleContext to module.[[Realm]].

View File

@ -15,12 +15,11 @@ describe("normal behavior", () => {
// Currently uses a plain JS::GlobalObject, i.e. no TestRunnerGlobalObject functions are available on the // Currently uses a plain JS::GlobalObject, i.e. no TestRunnerGlobalObject functions are available on the
// shadow realm's global object. This may change in the future, update the test accordingly. // shadow realm's global object. This may change in the future, update the test accordingly.
const shadowRealm = new ShadowRealm(); const shadowRealm = new ShadowRealm();
expect(shadowRealm.evaluate("globalThis.isStrictMode")).toBeUndefined(); expect(shadowRealm.evaluate("globalThis.markAsGarbage")).toBeUndefined();
}); });
test("strict mode behavior", () => { test("strict mode behavior", () => {
const shadowRealm = new ShadowRealm(); const shadowRealm = new ShadowRealm();
// NOTE: We don't have access to the isStrictMode() test helper inside the shadow realm, see the comment in the test above.
// sloppy mode // sloppy mode
expect(shadowRealm.evaluate("(function() { return !this; })()")).toBe(false); expect(shadowRealm.evaluate("(function() { return !this; })()")).toBe(false);

View File

@ -1,19 +0,0 @@
test("constructors are always strict mode", () => {
class A {
constructor() {
expect(isStrictMode()).toBeTrue();
}
}
new A();
});
test("methods are always strict mode", () => {
class A {
method() {
expect(isStrictMode()).toBeTrue();
}
}
new A().method();
});

View File

@ -110,33 +110,6 @@ test("arrow functions in objects", () => {
expect(foobar.x.z()).toBe(foobar.x); expect(foobar.x.z()).toBe(foobar.x);
}); });
test("strict mode propagation", () => {
(() => {
"use strict";
expect(isStrictMode()).toBeTrue();
(() => {
expect(isStrictMode()).toBeTrue();
})();
})();
(() => {
"use strict";
expect(isStrictMode()).toBeTrue();
})();
(() => {
expect(isStrictMode()).toBeFalse();
(() => {
"use strict";
expect(isStrictMode()).toBeTrue();
})();
expect(isStrictMode()).toBeFalse();
})();
});
test("no prototype", () => { test("no prototype", () => {
let foo = () => {}; let foo = () => {};
expect(foo).not.toHaveProperty("prototype"); expect(foo).not.toHaveProperty("prototype");

View File

@ -1,64 +1,3 @@
test("non strict-mode by default", () => {
expect(isStrictMode()).toBeFalse();
});
test("use strict with double quotes", () => {
"use strict";
expect(isStrictMode()).toBeTrue();
});
// prettier-ignore
test("use strict with single quotes", () => {
'use strict';
expect(isStrictMode()).toBeTrue();
});
// prettier-ignore
test("use strict with backticks does not yield strict mode", () => {
`use strict`;
expect(isStrictMode()).toBeFalse();
});
// prettier-ignore
test("use strict with single quotes after statement does not yield strict mode code", () => {
;'use strict';
expect(isStrictMode()).toBeFalse();
});
// prettier-ignore
test("use strict with double quotes after statement does not yield strict mode code", () => {
;"use strict";
expect(isStrictMode()).toBeFalse();
});
test("use strict interrupted by a line continuation does not yield strict mode code", () => {
"use \
strict";
expect(isStrictMode()).toBeFalse();
});
test("strict mode propagates down the scope chain", () => {
"use strict";
expect(isStrictMode()).toBeTrue();
(function () {
expect(isStrictMode()).toBeTrue();
})();
});
test("strict mode does not propagate up the scope chain", () => {
expect(isStrictMode()).toBeFalse();
(function () {
"use strict";
expect(isStrictMode()).toBeTrue();
})();
expect(isStrictMode()).toBeFalse();
});
test('only the string "use strict" yields strict mode code', () => {
"use stric";
expect(isStrictMode()).toBeFalse();
});
test("strict mode does not apply global object to |this|", () => { test("strict mode does not apply global object to |this|", () => {
"use strict"; "use strict";
let functionThis; let functionThis;

View File

@ -1,61 +0,0 @@
"do not use strict";
"no really";
// /\ Valid directives which should not trigger strict mode
test("basic functionality", () => {
expect(isStrictMode()).toBeFalse();
(function () {
expect(isStrictMode()).toBeFalse();
})();
(() => {
expect(isStrictMode()).toBeFalse();
})();
(() => {
expect(isStrictMode()).toBeFalse();
})();
function a() {
expect(isStrictMode()).toBeFalse();
}
a();
eval("expect(isStrictMode()).toBeFalse()");
});
test("functions with strict mode", () => {
expect(isStrictMode()).toBeFalse();
function a() {
"this is allowed trust me";
"use strict";
expect(isStrictMode()).toBeTrue();
}
a();
expect(isStrictMode()).toBeFalse();
(() => {
"use strict";
expect(isStrictMode()).toBeTrue();
})();
function b() {
eval("expect(isStrictMode()).toBeFalse()");
function nested() {
"use strict";
eval("expect(isStrictMode()).toBeTrue()");
}
nested();
eval("expect(isStrictMode()).toBeFalse()");
}
b();
});

View File

@ -1,26 +0,0 @@
"use strict";
test("basic functionality", () => {
expect(isStrictMode()).toBeTrue();
(function () {
expect(isStrictMode()).toBeTrue();
})();
(() => {
expect(isStrictMode()).toBeTrue();
})();
(() => {
"use strict";
expect(isStrictMode()).toBeTrue();
})();
function a() {
expect(isStrictMode()).toBeTrue();
}
a();
eval("expect(isStrictMode()).toBeTrue()");
});

View File

@ -1,25 +0,0 @@
test("Issue #3641, strict mode should be function- or program-level, not block-level", () => {
function func() {
expect(isStrictMode()).toBeFalse();
// prettier-ignore
{
"use strict";
expect(isStrictMode()).toBeFalse();
}
// prettier-ignore
if (true) {
"use strict";
expect(isStrictMode()).toBeFalse();
}
// prettier-ignore
do {
"use strict";
expect(isStrictMode()).toBeFalse();
} while (false);
}
func();
});

View File

@ -1,61 +0,0 @@
test("valid 'use strict; directive", () => {
expect(
(() => {
"use strict";
return isStrictMode();
})()
).toBeTrue();
expect(
// prettier-ignore
(() => {
'use strict';
return isStrictMode();
})()
).toBeTrue();
});
test("invalid 'use strict; directive", () => {
expect(
(() => {
" use strict ";
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
`use strict`;
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
"use\
strict";
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
"use\ strict";
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
"use \163trict";
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
`"use strict"`;
return isStrictMode();
})()
).toBeFalse();
expect(
(() => {
"use strict" + 1;
return isStrictMode();
})()
).toBeFalse();
});

View File

@ -17,11 +17,6 @@ TEST_ROOT("Libraries/LibJS/Tests");
TESTJS_PROGRAM_FLAG(test262_parser_tests, "Run test262 parser tests", "test262-parser-tests", 0); TESTJS_PROGRAM_FLAG(test262_parser_tests, "Run test262 parser tests", "test262-parser-tests", 0);
TESTJS_GLOBAL_FUNCTION(is_strict_mode, isStrictMode, 0)
{
return JS::Value(vm.running_execution_context().is_strict_mode);
}
TESTJS_GLOBAL_FUNCTION(can_parse_source, canParseSource) TESTJS_GLOBAL_FUNCTION(can_parse_source, canParseSource)
{ {
auto source = TRY(vm.argument(0).to_string(vm)); auto source = TRY(vm.argument(0).to_string(vm));