TemplateObject passed to template literal tags are not always identical for the same...
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 May 2019 22:15:59 +0000 (22:15 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 May 2019 22:15:59 +0000 (22:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190756

Reviewed by Saam Barati.

JSTests:

* complex.yaml:
* complex/tagged-template-regeneration-after.js: Added.
(shouldBe):
* complex/tagged-template-regeneration.js: Added.
(call):
(test):
* modules/tagged-template-inside-module.js: Added.
(from.string_appeared_here.call):
* modules/tagged-template-inside-module/other-tagged-templates.js: Added.
(call):
(export.otherTaggedTemplates):
* stress/call-and-construct-should-return-same-tagged-templates.js: Added.
(shouldBe):
(call):
(poly):
* stress/tagged-templates-in-direct-eval-should-not-produce-same-site-object.js: Added.
(shouldBe):
(call):
* stress/tagged-templates-in-function-in-direct-eval.js: Added.
(shouldBe):
(call):
(test):
* stress/tagged-templates-in-global-function-should-not-produce-same-site-object.js: Added.
(shouldBe):
(call):
* stress/tagged-templates-in-indirect-eval-should-not-produce-same-site-object.js: Added.
(shouldBe):
(call):
* stress/tagged-templates-in-multiple-functions.js: Added.
(shouldBe):
(call):
(a):
(b):
(c):
* stress/tagged-templates-with-same-start-offset.js: Added.
(shouldBe):

Source/JavaScriptCore:

Tagged template literal requires that the site object is allocated per source location. Previously, we create the site object
when linking CodeBlock and cache it in CodeBlock. But this is wrong because,

1. CodeBlock can be jettisoned and regenerated. So every time CodeBlock is regenerated, we get the different site object.
2. Call and Construct can have different CodeBlock. Even if the function is called in call-form or construct-form, we should return the same site object.

In this patch, we start caching these site objects in the top-level ScriptExecutable, this matches the spec's per source location since the only one top-level
ScriptExecutable is created for the given script code. Each ScriptExecutable of JSFunction can be created multiple times because CodeBlock creates it.
But the top-level one is not created by CodeBlock. This top-level ScriptExecutable is well-aligned to the Script itself. The top-level ScriptExecutable now has HashMap,
which maps source locations to cached site objects.

1. This patch threads the top-level ScriptExecutable to each FunctionExecutable creation. Each FunctionExecutable has a reference to the top-level ScriptExecutable.
2. We put TemplateObjectMap in ScriptExecutable, which manages cached template objects.
3. We move FunctionExecutable::m_cachedPolyProtoStructure to the FunctionExecutable::RareDate to keep FunctionExecutable 128 bytes.
4. TemplateObjectMap is indexed with endOffset of TaggedTemplate.

* Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Combined.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Separate.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Combined.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Separate.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Combined.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Separate.js-result:
* Scripts/tests/builtins/expected/JavaScriptCore-InternalClashingNames-Combined.js-result:
* Scripts/tests/builtins/expected/WebCore-AnotherGuardedInternalBuiltin-Separate.js-result:
* Scripts/tests/builtins/expected/WebCore-ArbitraryConditionalGuard-Separate.js-result:
* Scripts/tests/builtins/expected/WebCore-GuardedBuiltin-Separate.js-result:
* Scripts/tests/builtins/expected/WebCore-GuardedInternalBuiltin-Separate.js-result:
* Scripts/tests/builtins/expected/WebCore-UnguardedBuiltin-Separate.js-result:
* Scripts/tests/builtins/expected/WebCore-xmlCasingTest-Separate.js-result:
* Scripts/wkbuiltins/builtins_templates.py:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::setConstantRegisters):
* bytecode/CodeBlock.h:
* bytecode/UnlinkedFunctionExecutable.cpp:
(JSC::UnlinkedFunctionExecutable::link):
* bytecode/UnlinkedFunctionExecutable.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::addTemplateObjectConstant):
(JSC::BytecodeGenerator::emitGetTemplateObject):
* bytecompiler/BytecodeGenerator.h:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createTaggedTemplate):
* runtime/CachedTypes.cpp:
(JSC::CachedTemplateObjectDescriptor::encode):
(JSC::CachedTemplateObjectDescriptor::decode const):
(JSC::CachedJSValue::encode):
(JSC::CachedJSValue::decode const):
* runtime/EvalExecutable.cpp:
(JSC::EvalExecutable::ensureTemplateObjectMap):
(JSC::EvalExecutable::visitChildren):
* runtime/EvalExecutable.h:
* runtime/FunctionExecutable.cpp:
(JSC::FunctionExecutable::finishCreation):
(JSC::FunctionExecutable::visitChildren):
(JSC::FunctionExecutable::fromGlobalCode):
(JSC::FunctionExecutable::ensureRareDataSlow):
(JSC::FunctionExecutable::ensureTemplateObjectMap):
* runtime/FunctionExecutable.h:
* runtime/JSModuleRecord.cpp:
(JSC::JSModuleRecord::instantiateDeclarations):
* runtime/JSTemplateObjectDescriptor.cpp:
(JSC::JSTemplateObjectDescriptor::JSTemplateObjectDescriptor):
(JSC::JSTemplateObjectDescriptor::create):
* runtime/JSTemplateObjectDescriptor.h:
* runtime/ModuleProgramExecutable.cpp:
(JSC::ModuleProgramExecutable::ensureTemplateObjectMap):
(JSC::ModuleProgramExecutable::visitChildren):
* runtime/ModuleProgramExecutable.h:
* runtime/ProgramExecutable.cpp:
(JSC::ProgramExecutable::ensureTemplateObjectMap):
(JSC::ProgramExecutable::visitChildren):
* runtime/ProgramExecutable.h:
* runtime/ScriptExecutable.cpp:
(JSC::ScriptExecutable::topLevelExecutable):
(JSC::ScriptExecutable::createTemplateObject):
(JSC::ScriptExecutable::ensureTemplateObjectMapImpl):
(JSC::ScriptExecutable::ensureTemplateObjectMap):
* runtime/ScriptExecutable.h:
* tools/JSDollarVM.cpp:
(JSC::functionCreateBuiltin):
(JSC::functionDeleteAllCodeWhenIdle):
(JSC::JSDollarVM::finishCreation):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245040 268f45cc-cd09-0410-ab3c-d52691b4dbfc

50 files changed:
JSTests/ChangeLog
JSTests/complex.yaml
JSTests/complex/tagged-template-regeneration-after.js [new file with mode: 0644]
JSTests/complex/tagged-template-regeneration.js [new file with mode: 0644]
JSTests/modules/tagged-template-inside-module.js [new file with mode: 0644]
JSTests/modules/tagged-template-inside-module/other-tagged-templates.js [new file with mode: 0644]
JSTests/stress/call-and-construct-should-return-same-tagged-templates.js [new file with mode: 0644]
JSTests/stress/tagged-templates-in-direct-eval-should-not-produce-same-site-object.js [new file with mode: 0644]
JSTests/stress/tagged-templates-in-function-in-direct-eval.js [new file with mode: 0644]
JSTests/stress/tagged-templates-in-global-function-should-not-produce-same-site-object.js [new file with mode: 0644]
JSTests/stress/tagged-templates-in-indirect-eval-should-not-produce-same-site-object.js [new file with mode: 0644]
JSTests/stress/tagged-templates-in-multiple-functions.js [new file with mode: 0644]
JSTests/stress/tagged-templates-with-same-start-offset.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Combined.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Combined.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Combined.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/JavaScriptCore-InternalClashingNames-Combined.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-AnotherGuardedInternalBuiltin-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-ArbitraryConditionalGuard-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-GuardedBuiltin-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-GuardedInternalBuiltin-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-UnguardedBuiltin-Separate.js-result
Source/JavaScriptCore/Scripts/tests/builtins/expected/WebCore-xmlCasingTest-Separate.js-result
Source/JavaScriptCore/Scripts/wkbuiltins/builtins_templates.py
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/runtime/CachedTypes.cpp
Source/JavaScriptCore/runtime/EvalExecutable.cpp
Source/JavaScriptCore/runtime/EvalExecutable.h
Source/JavaScriptCore/runtime/FunctionExecutable.cpp
Source/JavaScriptCore/runtime/FunctionExecutable.h
Source/JavaScriptCore/runtime/JSModuleRecord.cpp
Source/JavaScriptCore/runtime/JSTemplateObjectDescriptor.cpp
Source/JavaScriptCore/runtime/JSTemplateObjectDescriptor.h
Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp
Source/JavaScriptCore/runtime/ModuleProgramExecutable.h
Source/JavaScriptCore/runtime/ProgramExecutable.cpp
Source/JavaScriptCore/runtime/ProgramExecutable.h
Source/JavaScriptCore/runtime/ScriptExecutable.cpp
Source/JavaScriptCore/runtime/ScriptExecutable.h
Source/JavaScriptCore/tools/JSDollarVM.cpp

index 65a62aa..e87236a 100644 (file)
@@ -1,3 +1,47 @@
+2019-05-07  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        TemplateObject passed to template literal tags are not always identical for the same source location.
+        https://bugs.webkit.org/show_bug.cgi?id=190756
+
+        Reviewed by Saam Barati.
+
+        * complex.yaml:
+        * complex/tagged-template-regeneration-after.js: Added.
+        (shouldBe):
+        * complex/tagged-template-regeneration.js: Added.
+        (call):
+        (test):
+        * modules/tagged-template-inside-module.js: Added.
+        (from.string_appeared_here.call):
+        * modules/tagged-template-inside-module/other-tagged-templates.js: Added.
+        (call):
+        (export.otherTaggedTemplates):
+        * stress/call-and-construct-should-return-same-tagged-templates.js: Added.
+        (shouldBe):
+        (call):
+        (poly):
+        * stress/tagged-templates-in-direct-eval-should-not-produce-same-site-object.js: Added.
+        (shouldBe):
+        (call):
+        * stress/tagged-templates-in-function-in-direct-eval.js: Added.
+        (shouldBe):
+        (call):
+        (test):
+        * stress/tagged-templates-in-global-function-should-not-produce-same-site-object.js: Added.
+        (shouldBe):
+        (call):
+        * stress/tagged-templates-in-indirect-eval-should-not-produce-same-site-object.js: Added.
+        (shouldBe):
+        (call):
+        * stress/tagged-templates-in-multiple-functions.js: Added.
+        (shouldBe):
+        (call):
+        (a):
+        (b):
+        (c):
+        * stress/tagged-templates-with-same-start-offset.js: Added.
+        (shouldBe):
+
 2019-05-07  Robin Morisset  <rmorisset@apple.com>
 
         All prototypes should call didBecomePrototype()
index ce038f4..1061350 100644 (file)
@@ -25,3 +25,6 @@
 
 - path: complex/generator-regeneration.js
   cmd: runComplexTest [], ["generator-regeneration-after.js"], "--useDollarVM=1"
+
+- path: complex/tagged-template-regeneration.js
+  cmd: runComplexTest [], ["tagged-template-regeneration-after.js"], "--useDollarVM=1"
diff --git a/JSTests/complex/tagged-template-regeneration-after.js b/JSTests/complex/tagged-template-regeneration-after.js
new file mode 100644 (file)
index 0000000..6fc7f02
--- /dev/null
@@ -0,0 +1,7 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var second = test();
+shouldBe(first, second);
diff --git a/JSTests/complex/tagged-template-regeneration.js b/JSTests/complex/tagged-template-regeneration.js
new file mode 100644 (file)
index 0000000..ec52ab6
--- /dev/null
@@ -0,0 +1,13 @@
+function call(site)
+{
+    return site;
+}
+
+function test()
+{
+    return call`Cocoa`;
+}
+
+var first = test();
+$vm.deleteAllCodeWhenIdle();
+fullGC();
diff --git a/JSTests/modules/tagged-template-inside-module.js b/JSTests/modules/tagged-template-inside-module.js
new file mode 100644 (file)
index 0000000..aa747fc
--- /dev/null
@@ -0,0 +1,11 @@
+import { shouldThrow, shouldBe } from "./resources/assert.js";
+import { otherTaggedTemplates } from "./tagged-template-inside-module/other-tagged-templates.js"
+
+function call(site) {
+    return site;
+}
+
+var template = otherTaggedTemplates();
+shouldBe(call`Cocoa` !== template, true);
+shouldBe(template, otherTaggedTemplates());
+shouldBe(template, new otherTaggedTemplates());
diff --git a/JSTests/modules/tagged-template-inside-module/other-tagged-templates.js b/JSTests/modules/tagged-template-inside-module/other-tagged-templates.js
new file mode 100644 (file)
index 0000000..5d167a3
--- /dev/null
@@ -0,0 +1,9 @@
+function call(site)
+{
+    return site;
+}
+
+export function otherTaggedTemplates()
+{
+    return call`Cocoa`;
+}
diff --git a/JSTests/stress/call-and-construct-should-return-same-tagged-templates.js b/JSTests/stress/call-and-construct-should-return-same-tagged-templates.js
new file mode 100644 (file)
index 0000000..8338ce5
--- /dev/null
@@ -0,0 +1,20 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function call(site)
+{
+    return site;
+}
+
+function poly()
+{
+    return call`Cocoa`;
+}
+
+var first = poly();
+var second = new poly();
+
+shouldBe(first, second);
diff --git a/JSTests/stress/tagged-templates-in-direct-eval-should-not-produce-same-site-object.js b/JSTests/stress/tagged-templates-in-direct-eval-should-not-produce-same-site-object.js
new file mode 100644 (file)
index 0000000..fa48760
--- /dev/null
@@ -0,0 +1,14 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function call(site)
+{
+    return site;
+}
+
+var expr = "call`Cocoa`";
+var first = eval(expr);
+var second = eval(expr);
+shouldBe(first !== second, true);
diff --git a/JSTests/stress/tagged-templates-in-function-in-direct-eval.js b/JSTests/stress/tagged-templates-in-function-in-direct-eval.js
new file mode 100644 (file)
index 0000000..eed24f6
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function call(site)
+{
+    return site;
+}
+
+function test()
+{
+    return eval("(function ok() { return call`Cocoa`; })()");
+}
+
+var first = test();
+var second = test();
+shouldBe(first !== second, true);
diff --git a/JSTests/stress/tagged-templates-in-global-function-should-not-produce-same-site-object.js b/JSTests/stress/tagged-templates-in-global-function-should-not-produce-same-site-object.js
new file mode 100644 (file)
index 0000000..53e76ed
--- /dev/null
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function call(site)
+{
+    return site;
+}
+
+var expr = "return call`Cocoa`";
+var firstFunction = Function(expr);
+var secondFunction = Function(expr);
+var first = firstFunction();
+var second = secondFunction();
+shouldBe(first !== second, true);
+
+shouldBe(first, firstFunction());
+shouldBe(first, new firstFunction());
+shouldBe(second, secondFunction());
+shouldBe(second, new secondFunction());
diff --git a/JSTests/stress/tagged-templates-in-indirect-eval-should-not-produce-same-site-object.js b/JSTests/stress/tagged-templates-in-indirect-eval-should-not-produce-same-site-object.js
new file mode 100644 (file)
index 0000000..aeacb1b
--- /dev/null
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var indirectEval = eval;
+function call(site)
+{
+    return site;
+}
+
+var expr = "call`Cocoa`";
+var first = indirectEval(expr);
+var second = indirectEval(expr);
+shouldBe(first !== second, true);
diff --git a/JSTests/stress/tagged-templates-in-multiple-functions.js b/JSTests/stress/tagged-templates-in-multiple-functions.js
new file mode 100644 (file)
index 0000000..3828f53
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function call(site)
+{
+    return site;
+}
+
+function a()
+{
+    return call`Cocoa`;
+}
+
+function b()
+{
+    return call`Cocoa`;
+}
+
+function c()
+{
+    return [ call`Cocoa`, call`Cocoa` ];
+}
+
+shouldBe(a() !== b(), true);
+shouldBe(a() === a(), true);
+shouldBe(b() === b(), true);
+var result = c();
+shouldBe(c()[0] === result[0], true);
+shouldBe(c()[1] === result[1], true);
+shouldBe(result[0] !== result[1], true);
diff --git a/JSTests/stress/tagged-templates-with-same-start-offset.js b/JSTests/stress/tagged-templates-with-same-start-offset.js
new file mode 100644 (file)
index 0000000..2248ff2
--- /dev/null
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var sites = [];
+
+function call(site)
+{
+    sites.push(site);
+    return call;
+}
+
+call`Hello``New``World`
+
+shouldBe(sites.length, 3);
+
+shouldBe(sites[0] !== sites[1], true);
+shouldBe(sites[0] !== sites[2], true);
+shouldBe(sites[1] !== sites[2], true);
+
+shouldBe(sites[0].length, 1);
+shouldBe(sites[0][0], `Hello`);
+
+shouldBe(sites[1].length, 1);
+shouldBe(sites[1][0], `New`);
+
+shouldBe(sites[2].length, 1);
+shouldBe(sites[2][0], `World`);
index ada1cfa..e1d86e5 100644 (file)
@@ -1,3 +1,94 @@
+2019-05-07  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        TemplateObject passed to template literal tags are not always identical for the same source location.
+        https://bugs.webkit.org/show_bug.cgi?id=190756
+
+        Reviewed by Saam Barati.
+
+        Tagged template literal requires that the site object is allocated per source location. Previously, we create the site object
+        when linking CodeBlock and cache it in CodeBlock. But this is wrong because,
+
+        1. CodeBlock can be jettisoned and regenerated. So every time CodeBlock is regenerated, we get the different site object.
+        2. Call and Construct can have different CodeBlock. Even if the function is called in call-form or construct-form, we should return the same site object.
+
+        In this patch, we start caching these site objects in the top-level ScriptExecutable, this matches the spec's per source location since the only one top-level
+        ScriptExecutable is created for the given script code. Each ScriptExecutable of JSFunction can be created multiple times because CodeBlock creates it.
+        But the top-level one is not created by CodeBlock. This top-level ScriptExecutable is well-aligned to the Script itself. The top-level ScriptExecutable now has HashMap,
+        which maps source locations to cached site objects.
+
+        1. This patch threads the top-level ScriptExecutable to each FunctionExecutable creation. Each FunctionExecutable has a reference to the top-level ScriptExecutable.
+        2. We put TemplateObjectMap in ScriptExecutable, which manages cached template objects.
+        3. We move FunctionExecutable::m_cachedPolyProtoStructure to the FunctionExecutable::RareDate to keep FunctionExecutable 128 bytes.
+        4. TemplateObjectMap is indexed with endOffset of TaggedTemplate.
+
+        * Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Combined.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-Builtin.Promise-Separate.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Combined.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-Builtin.prototype-Separate.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Combined.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-BuiltinConstructor-Separate.js-result:
+        * Scripts/tests/builtins/expected/JavaScriptCore-InternalClashingNames-Combined.js-result:
+        * Scripts/tests/builtins/expected/WebCore-AnotherGuardedInternalBuiltin-Separate.js-result:
+        * Scripts/tests/builtins/expected/WebCore-ArbitraryConditionalGuard-Separate.js-result:
+        * Scripts/tests/builtins/expected/WebCore-GuardedBuiltin-Separate.js-result:
+        * Scripts/tests/builtins/expected/WebCore-GuardedInternalBuiltin-Separate.js-result:
+        * Scripts/tests/builtins/expected/WebCore-UnguardedBuiltin-Separate.js-result:
+        * Scripts/tests/builtins/expected/WebCore-xmlCasingTest-Separate.js-result:
+        * Scripts/wkbuiltins/builtins_templates.py:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        (JSC::CodeBlock::setConstantRegisters):
+        * bytecode/CodeBlock.h:
+        * bytecode/UnlinkedFunctionExecutable.cpp:
+        (JSC::UnlinkedFunctionExecutable::link):
+        * bytecode/UnlinkedFunctionExecutable.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::addTemplateObjectConstant):
+        (JSC::BytecodeGenerator::emitGetTemplateObject):
+        * bytecompiler/BytecodeGenerator.h:
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createTaggedTemplate):
+        * runtime/CachedTypes.cpp:
+        (JSC::CachedTemplateObjectDescriptor::encode):
+        (JSC::CachedTemplateObjectDescriptor::decode const):
+        (JSC::CachedJSValue::encode):
+        (JSC::CachedJSValue::decode const):
+        * runtime/EvalExecutable.cpp:
+        (JSC::EvalExecutable::ensureTemplateObjectMap):
+        (JSC::EvalExecutable::visitChildren):
+        * runtime/EvalExecutable.h:
+        * runtime/FunctionExecutable.cpp:
+        (JSC::FunctionExecutable::finishCreation):
+        (JSC::FunctionExecutable::visitChildren):
+        (JSC::FunctionExecutable::fromGlobalCode):
+        (JSC::FunctionExecutable::ensureRareDataSlow):
+        (JSC::FunctionExecutable::ensureTemplateObjectMap):
+        * runtime/FunctionExecutable.h:
+        * runtime/JSModuleRecord.cpp:
+        (JSC::JSModuleRecord::instantiateDeclarations):
+        * runtime/JSTemplateObjectDescriptor.cpp:
+        (JSC::JSTemplateObjectDescriptor::JSTemplateObjectDescriptor):
+        (JSC::JSTemplateObjectDescriptor::create):
+        * runtime/JSTemplateObjectDescriptor.h:
+        * runtime/ModuleProgramExecutable.cpp:
+        (JSC::ModuleProgramExecutable::ensureTemplateObjectMap):
+        (JSC::ModuleProgramExecutable::visitChildren):
+        * runtime/ModuleProgramExecutable.h:
+        * runtime/ProgramExecutable.cpp:
+        (JSC::ProgramExecutable::ensureTemplateObjectMap):
+        (JSC::ProgramExecutable::visitChildren):
+        * runtime/ProgramExecutable.h:
+        * runtime/ScriptExecutable.cpp:
+        (JSC::ScriptExecutable::topLevelExecutable):
+        (JSC::ScriptExecutable::createTemplateObject):
+        (JSC::ScriptExecutable::ensureTemplateObjectMapImpl):
+        (JSC::ScriptExecutable::ensureTemplateObjectMap):
+        * runtime/ScriptExecutable.h:
+        * tools/JSDollarVM.cpp:
+        (JSC::functionCreateBuiltin):
+        (JSC::functionDeleteAllCodeWhenIdle):
+        (JSC::JSDollarVM::finishCreation):
+
 2019-05-07  Robin Morisset  <rmorisset@apple.com>
 
         [B3] Constants should be hoisted to the root block until moveConstants
index 8ab7656..8d30ca9 100644 (file)
@@ -141,7 +141,7 @@ s_JSCCombinedCode + 412
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index bcedfb9..048a601 100644 (file)
@@ -158,7 +158,7 @@ const char* const s_builtinPromiseFulfillPromiseCode =
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN.PROMISE_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index 215ec8e..3dedbe1 100644 (file)
@@ -167,7 +167,7 @@ s_JSCCombinedCode + 2694
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index ebb2e72..8ff897f 100644 (file)
@@ -282,7 +282,7 @@ const char* const s_builtinPrototypeTestCode =
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN.PROTOTYPE_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index be3a59a..1d8345f 100644 (file)
@@ -139,7 +139,7 @@ s_JSCCombinedCode + 2046
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index 454861f..a6ddd91 100644 (file)
@@ -212,7 +212,7 @@ const char* const s_builtinConstructorFromCode =
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTINCONSTRUCTOR_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index bfb2b2e..b5feac2 100644 (file)
@@ -140,7 +140,7 @@ s_JSCCombinedCode + 71
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); }
 JSC_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
 
index e46d23a..043a993 100644 (file)
@@ -220,7 +220,7 @@ const char* const s_anotherGuardedInternalBuiltinLetsFetchCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().anotherGuardedInternalBuiltinBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().anotherGuardedInternalBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().anotherGuardedInternalBuiltinBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().anotherGuardedInternalBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_ANOTHERGUARDEDINTERNALBUILTIN_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index be0709a..a42fcfb 100644 (file)
@@ -190,7 +190,7 @@ const char* const s_arbitraryConditionalGuardIsReadableStreamLockedCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().arbitraryConditionalGuardBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().arbitraryConditionalGuardBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().arbitraryConditionalGuardBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().arbitraryConditionalGuardBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_ARBITRARYCONDITIONALGUARD_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 5567dfa..5896e07 100644 (file)
@@ -190,7 +190,7 @@ const char* const s_guardedBuiltinIsReadableStreamLockedCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().guardedBuiltinBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().guardedBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().guardedBuiltinBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().guardedBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_GUARDEDBUILTIN_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 6adaf32..cc191ed 100644 (file)
@@ -222,7 +222,7 @@ const char* const s_guardedInternalBuiltinIsReadableStreamLockedCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().guardedInternalBuiltinBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().guardedInternalBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().guardedInternalBuiltinBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().guardedInternalBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_GUARDEDINTERNALBUILTIN_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 8bc20fb..0c091eb 100644 (file)
@@ -184,7 +184,7 @@ const char* const s_unguardedBuiltinIsReadableStreamLockedCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().unguardedBuiltinBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().unguardedBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().unguardedBuiltinBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().unguardedBuiltinBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_UNGUARDEDBUILTIN_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 02d4417..fe2eff5 100644 (file)
@@ -275,7 +275,7 @@ const char* const s_xmlCasingTestUrlCasingTestCode =
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
 {\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \
-    return clientData->builtinFunctions().xmlCasingTestBuiltins().codeName##Executable()->link(vm, clientData->builtinFunctions().xmlCasingTestBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return clientData->builtinFunctions().xmlCasingTestBuiltins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().xmlCasingTestBuiltins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 WEBCORE_FOREACH_XMLCASINGTEST_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 4c293cc..3665cb5 100644 (file)
@@ -85,7 +85,7 @@ ${macroPrefix}_FOREACH_${objectMacro}_BUILTIN_CODE(DECLARE_BUILTIN_GENERATOR)
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \\
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\
 {\\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 ${macroPrefix}_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
@@ -96,7 +96,7 @@ ${macroPrefix}_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \\
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\
 {\\
-    return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
+    return vm.builtinExecutables()->codeName##Executable()->link(vm, nullptr, vm.builtinExecutables()->codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \
 }
 ${macroPrefix}_FOREACH_${objectMacro}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
@@ -108,7 +108,7 @@ ${macroPrefix}_FOREACH_${objectMacro}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\
 {\\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \\
-    return clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Executable()->link(vm, clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \\
+    return clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \\
 }
 ${macroPrefix}_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
@@ -120,7 +120,7 @@ ${macroPrefix}_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\
 {\\
     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \\
-    return clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Executable()->link(vm, clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \\
+    return clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().${objectNameLC}Builtins().codeName##Source(), WTF::nullopt, s_##codeName##Intrinsic); \\
 }
 ${macroPrefix}_FOREACH_${objectMacro}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR)
 #undef DEFINE_BUILTIN_GENERATOR
index 7b191cd..46f3ed7 100644 (file)
@@ -395,7 +395,8 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
     if (m_unlinkedCode->wasCompiledWithTypeProfilerOpcodes() || m_unlinkedCode->wasCompiledWithControlFlowProfilerOpcodes())
         vm.functionHasExecutedCache()->removeUnexecutedRange(ownerExecutable->sourceID(), ownerExecutable->typeProfilingStartOffset(vm), ownerExecutable->typeProfilingEndOffset(vm));
 
-    setConstantRegisters(unlinkedCodeBlock->constantRegisters(), unlinkedCodeBlock->constantsSourceCodeRepresentation());
+    ScriptExecutable* topLevelExecutable = ownerExecutable->topLevelExecutable();
+    setConstantRegisters(unlinkedCodeBlock->constantRegisters(), unlinkedCodeBlock->constantsSourceCodeRepresentation(), topLevelExecutable);
     RETURN_IF_EXCEPTION(throwScope, false);
 
     for (unsigned i = 0; i < LinkTimeConstantCount; i++) {
@@ -421,7 +422,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i);
         if (shouldUpdateFunctionHasExecutedCache)
             vm.functionHasExecutedCache()->insertUnexecutedRange(ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
-        m_functionDecls[i].set(vm, this, unlinkedExecutable->link(vm, ownerExecutable->source()));
+        m_functionDecls[i].set(vm, this, unlinkedExecutable->link(vm, topLevelExecutable, ownerExecutable->source()));
     }
 
     m_functionExprs = RefCountedArray<WriteBarrier<FunctionExecutable>>(unlinkedCodeBlock->numberOfFunctionExprs());
@@ -429,7 +430,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionExpr(i);
         if (shouldUpdateFunctionHasExecutedCache)
             vm.functionHasExecutedCache()->insertUnexecutedRange(ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
-        m_functionExprs[i].set(vm, this, unlinkedExecutable->link(vm, ownerExecutable->source()));
+        m_functionExprs[i].set(vm, this, unlinkedExecutable->link(vm, topLevelExecutable, ownerExecutable->source()));
     }
 
     if (unlinkedCodeBlock->hasRareData()) {
@@ -870,7 +871,7 @@ void CodeBlock::setConstantIdentifierSetRegisters(VM& vm, const Vector<ConstantI
     }
 }
 
-void CodeBlock::setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation)
+void CodeBlock::setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation, ScriptExecutable* topLevelExecutable)
 {
     VM& vm = *m_vm;
     auto scope = DECLARE_THROW_SCOPE(vm);
@@ -898,7 +899,7 @@ void CodeBlock::setConstantRegisters(const Vector<WriteBarrier<Unknown>>& consta
 
                     constant = clone;
                 } else if (auto* descriptor = jsDynamicCast<JSTemplateObjectDescriptor*>(vm, cell)) {
-                    auto* templateObject = descriptor->createTemplateObject(exec);
+                    auto* templateObject = topLevelExecutable->createTemplateObject(exec, descriptor);
                     RETURN_IF_EXCEPTION(scope, void());
                     constant = templateObject;
                 }
index 12f9082..406fca0 100644 (file)
@@ -911,7 +911,7 @@ private:
 
     void setConstantIdentifierSetRegisters(VM&, const Vector<ConstantIdentifierSetEntry>& constants);
 
-    void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation);
+    void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation, ScriptExecutable* topLevelExecutable);
 
     void replaceConstant(int index, JSValue value)
     {
index 61449db..5e7148e 100644 (file)
@@ -158,7 +158,7 @@ SourceCode UnlinkedFunctionExecutable::linkedSourceCode(const SourceCode& passed
     return SourceCode(parentSource.provider(), startOffset, startOffset + m_sourceLength, firstLine, startColumn);
 }
 
-FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, const SourceCode& passedParentSource, Optional<int> overrideLineNumber, Intrinsic intrinsic)
+FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, ScriptExecutable* topLevelExecutable, const SourceCode& passedParentSource, Optional<int> overrideLineNumber, Intrinsic intrinsic)
 {
     SourceCode source = linkedSourceCode(passedParentSource);
     FunctionOverrides::OverrideInfo overrideInfo;
@@ -166,7 +166,7 @@ FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, const SourceCode& p
     if (UNLIKELY(Options::functionOverrides()))
         hasFunctionOverride = FunctionOverrides::initializeOverrideFor(source, overrideInfo);
 
-    FunctionExecutable* result = FunctionExecutable::create(vm, source, this, intrinsic);
+    FunctionExecutable* result = FunctionExecutable::create(vm, topLevelExecutable, source, this, intrinsic);
     if (overrideLineNumber)
         result->setOverrideLineNumber(*overrideLineNumber);
 
index 1b03597..8e0abed 100644 (file)
@@ -126,7 +126,7 @@ public:
         int overrideLineNumber, Optional<int> functionConstructorParametersEndPosition);
 
     SourceCode linkedSourceCode(const SourceCode&) const;
-    JS_EXPORT_PRIVATE FunctionExecutable* link(VM&, const SourceCode& parentSource, Optional<int> overrideLineNumber = WTF::nullopt, Intrinsic = NoIntrinsic);
+    JS_EXPORT_PRIVATE FunctionExecutable* link(VM&, ScriptExecutable* topLevelExecutable, const SourceCode& parentSource, Optional<int> overrideLineNumber = WTF::nullopt, Intrinsic = NoIntrinsic);
 
     void clearCode(VM& vm)
     {
index 35409a6..a5aed4a 100644 (file)
@@ -2948,12 +2948,12 @@ JSString* BytecodeGenerator::addStringConstant(const Identifier& identifier)
     return stringInMap;
 }
 
-RegisterID* BytecodeGenerator::addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&& descriptor)
+RegisterID* BytecodeGenerator::addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&& descriptor, int endOffset)
 {
-    JSTemplateObjectDescriptor* descriptorValue = m_templateObjectDescriptorMap.ensure(descriptor.copyRef(), [&] {
-        return JSTemplateObjectDescriptor::create(*vm(), WTFMove(descriptor));
+    auto result = m_templateObjectDescriptorSet.add(WTFMove(descriptor));
+    JSTemplateObjectDescriptor* descriptorValue = m_templateDescriptorMap.ensure(endOffset, [&] {
+        return JSTemplateObjectDescriptor::create(*vm(), result.iterator->copyRef(), endOffset);
     }).iterator->value;
-
     int index = addConstantIndex();
     m_codeBlock->addConstant(descriptorValue);
     return &m_constantPoolRegisters[index];
@@ -4141,7 +4141,7 @@ RegisterID* BytecodeGenerator::emitGetTemplateObject(RegisterID* dst, TaggedTemp
         else
             cookedStrings.append(string->cooked()->impl());
     }
-    RefPtr<RegisterID> constant = addTemplateObjectConstant(TemplateObjectDescriptor::create(WTFMove(rawStrings), WTFMove(cookedStrings)));
+    RefPtr<RegisterID> constant = addTemplateObjectConstant(TemplateObjectDescriptor::create(WTFMove(rawStrings), WTFMove(cookedStrings)), taggedTemplate->endOffset());
     if (!dst)
         return constant.get();
     return move(dst, constant.get());
index 4426b51..29ed39a 100644 (file)
@@ -1060,7 +1060,8 @@ namespace JSC {
         using NumberMap = HashMap<double, JSValue>;
         using IdentifierStringMap = HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash>;
         using IdentifierBigIntMap = HashMap<BigIntMapEntry, JSBigInt*>;
-        using TemplateObjectDescriptorMap = HashMap<Ref<TemplateObjectDescriptor>, JSTemplateObjectDescriptor*>;
+        using TemplateObjectDescriptorSet = HashSet<Ref<TemplateObjectDescriptor>>;
+        using TemplateDescriptorMap = HashMap<uint64_t, JSTemplateObjectDescriptor*, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>;
 
         // Helper for emitCall() and emitConstruct(). This works because the set of
         // expected functions have identical behavior for both call and construct
@@ -1152,7 +1153,7 @@ namespace JSC {
     public:
         JSString* addStringConstant(const Identifier&);
         JSValue addBigIntConstant(const Identifier&, uint8_t radix, bool sign);
-        RegisterID* addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&&);
+        RegisterID* addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&&, int);
 
         const InstructionStream& instructions() const { return m_writer; }
 
@@ -1270,7 +1271,8 @@ namespace JSC {
         JSValueMap m_jsValueMap;
         IdentifierStringMap m_stringMap;
         IdentifierBigIntMap m_bigIntMap;
-        TemplateObjectDescriptorMap m_templateObjectDescriptorMap;
+        TemplateObjectDescriptorSet m_templateObjectDescriptorSet;
+        TemplateDescriptorMap m_templateDescriptorMap;
 
         StaticPropertyAnalyzer m_staticPropertyAnalyzer;
 
index 5f98503..104e59b 100644 (file)
@@ -331,6 +331,7 @@ public:
     {
         auto node = new (m_parserArena) TaggedTemplateNode(location, base, templateLiteral);
         setExceptionLocation(node, start, divot, end);
+        setEndOffset(node, end.offset);
         return node;
     }
 
index 93f2224..cbbf5db 100644 (file)
@@ -1152,24 +1152,26 @@ private:
 
 class CachedTemplateObjectDescriptor : public CachedObject<TemplateObjectDescriptor> {
 public:
-    void encode(Encoder& encoder, const TemplateObjectDescriptor& templateObjectDescriptor)
+    void encode(Encoder& encoder, const JSTemplateObjectDescriptor& descriptor)
     {
-        m_rawStrings.encode(encoder, templateObjectDescriptor.rawStrings());
-        m_cookedStrings.encode(encoder, templateObjectDescriptor.cookedStrings());
+        m_rawStrings.encode(encoder, descriptor.descriptor().rawStrings());
+        m_cookedStrings.encode(encoder, descriptor.descriptor().cookedStrings());
+        m_endOffset = descriptor.endOffset();
     }
 
-    Ref<TemplateObjectDescriptor> decode(Decoder& decoder) const
+    JSTemplateObjectDescriptor* decode(Decoder& decoder) const
     {
         TemplateObjectDescriptor::StringVector decodedRawStrings;
         TemplateObjectDescriptor::OptionalStringVector decodedCookedStrings;
         m_rawStrings.decode(decoder, decodedRawStrings);
         m_cookedStrings.decode(decoder, decodedCookedStrings);
-        return TemplateObjectDescriptor::create(WTFMove(decodedRawStrings), WTFMove(decodedCookedStrings));
+        return JSTemplateObjectDescriptor::create(decoder.vm(), TemplateObjectDescriptor::create(WTFMove(decodedRawStrings), WTFMove(decodedCookedStrings)), m_endOffset);
     }
 
 private:
     CachedVector<CachedString, 4> m_rawStrings;
     CachedVector<CachedOptional<CachedString>, 4> m_cookedStrings;
+    int m_endOffset;
 };
 
 class CachedBigInt : public VariableLengthObject<JSBigInt> {
@@ -1243,7 +1245,7 @@ public:
 
         if (auto* templateObjectDescriptor = jsDynamicCast<JSTemplateObjectDescriptor*>(vm, cell)) {
             m_type = EncodedType::TemplateObjectDescriptor;
-            this->allocate<CachedTemplateObjectDescriptor>(encoder)->encode(encoder, templateObjectDescriptor->descriptor());
+            this->allocate<CachedTemplateObjectDescriptor>(encoder)->encode(encoder, *templateObjectDescriptor);
             return;
         }
 
@@ -1278,7 +1280,7 @@ public:
             v = this->buffer<CachedRegExp>()->decode(decoder);
             break;
         case EncodedType::TemplateObjectDescriptor:
-            v = JSTemplateObjectDescriptor::create(decoder.vm(), this->buffer<CachedTemplateObjectDescriptor>()->decode(decoder));
+            v = this->buffer<CachedTemplateObjectDescriptor>()->decode(decoder);
             break;
         case EncodedType::BigInt:
             v = this->buffer<CachedBigInt>()->decode(decoder);
index 28c7f76..e2ca408 100644 (file)
@@ -44,6 +44,11 @@ void EvalExecutable::destroy(JSCell* cell)
     static_cast<EvalExecutable*>(cell)->EvalExecutable::~EvalExecutable();
 }
 
+auto EvalExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
+{
+    return ensureTemplateObjectMapImpl(m_templateObjectMap);
+}
+
 void EvalExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     EvalExecutable* thisObject = jsCast<EvalExecutable*>(cell);
@@ -51,6 +56,11 @@ void EvalExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     Base::visitChildren(thisObject, visitor);
     visitor.append(thisObject->m_unlinkedEvalCodeBlock);
     visitor.append(thisObject->m_evalCodeBlock);
+    if (TemplateObjectMap* map = thisObject->m_templateObjectMap.get()) {
+        auto locker = holdLock(thisObject->cellLock());
+        for (auto& entry : *map)
+            visitor.append(entry.value);
+    }
 }
 
 } // namespace JSC
index b9cb74d..57b7a35 100644 (file)
@@ -69,6 +69,8 @@ public:
     unsigned numTopLevelFunctionDecls() { return m_unlinkedEvalCodeBlock->numberOfFunctionDecls(); }
     bool allowDirectEvalCache() const { return m_unlinkedEvalCodeBlock->allowDirectEvalCache(); }
 
+    TemplateObjectMap& ensureTemplateObjectMap(VM&);
+
 protected:
     friend class ExecutableBase;
     friend class ScriptExecutable;
@@ -80,6 +82,7 @@ protected:
 
     WriteBarrier<ExecutableToCodeBlockEdge> m_evalCodeBlock;
     WriteBarrier<UnlinkedEvalCodeBlock> m_unlinkedEvalCodeBlock;
+    std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
 };
 
 } // namespace JSC
index 2e85f47..af3ab88 100644 (file)
@@ -54,9 +54,10 @@ FunctionExecutable::FunctionExecutable(VM& vm, const SourceCode& source, Unlinke
         m_singletonFunctionState = ClearWatchpoint;
 }
 
-void FunctionExecutable::finishCreation(VM& vm)
+void FunctionExecutable::finishCreation(VM& vm, ScriptExecutable* topLevelExecutable)
 {
     Base::finishCreation(vm);
+    m_topLevelExecutable.set(vm, this, topLevelExecutable ? topLevelExecutable : this);
     if (VM::canUseJIT())
         m_singletonFunction.set(vm, this, InferredValue::create(vm));
 }
@@ -85,12 +86,20 @@ void FunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     FunctionExecutable* thisObject = jsCast<FunctionExecutable*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
+    visitor.append(thisObject->m_topLevelExecutable);
     visitor.append(thisObject->m_codeBlockForCall);
     visitor.append(thisObject->m_codeBlockForConstruct);
     visitor.append(thisObject->m_unlinkedExecutable);
     if (VM::canUseJIT())
         visitor.append(thisObject->m_singletonFunction);
-    visitor.append(thisObject->m_cachedPolyProtoStructure);
+    if (RareData* rareData = thisObject->m_rareData.get()) {
+        visitor.append(rareData->m_cachedPolyProtoStructure);
+        if (TemplateObjectMap* map = rareData->m_templateObjectMap.get()) {
+            auto locker = holdLock(thisObject->cellLock());
+            for (auto& entry : *map)
+                visitor.append(entry.value);
+        }
+    }
 }
 
 FunctionExecutable* FunctionExecutable::fromGlobalCode(
@@ -103,7 +112,7 @@ FunctionExecutable* FunctionExecutable::fromGlobalCode(
     if (!unlinkedExecutable)
         return nullptr;
 
-    return unlinkedExecutable->link(exec.vm(), source, overrideLineNumber);
+    return unlinkedExecutable->link(exec.vm(), nullptr, source, overrideLineNumber);
 }
 
 FunctionExecutable::RareData& FunctionExecutable::ensureRareDataSlow()
@@ -115,6 +124,7 @@ FunctionExecutable::RareData& FunctionExecutable::ensureRareDataSlow()
     rareData->m_parametersStartOffset = parametersStartOffset();
     rareData->m_typeProfilingStartOffset = typeProfilingStartOffset();
     rareData->m_typeProfilingEndOffset = typeProfilingEndOffset();
+    WTF::storeStoreFence();
     m_rareData = WTFMove(rareData);
     return *m_rareData;
 }
@@ -130,4 +140,10 @@ void FunctionExecutable::overrideInfo(const FunctionOverrideInfo& overrideInfo)
     rareData.m_typeProfilingEndOffset = overrideInfo.typeProfilingEndOffset;
 }
 
+auto FunctionExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
+{
+    RareData& rareData = ensureRareData();
+    return ensureTemplateObjectMapImpl(rareData.m_templateObjectMap);
+}
+
 } // namespace JSC
index 7cfedbd..6fb5ccd 100644 (file)
@@ -48,10 +48,10 @@ public:
         return &vm.functionExecutableSpace.space;
     }
 
-    static FunctionExecutable* create(VM& vm, const SourceCode& source, UnlinkedFunctionExecutable* unlinkedExecutable, Intrinsic intrinsic)
+    static FunctionExecutable* create(VM& vm, ScriptExecutable* topLevelExecutable, const SourceCode& source, UnlinkedFunctionExecutable* unlinkedExecutable, Intrinsic intrinsic)
     {
         FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(vm.heap)) FunctionExecutable(vm, source, unlinkedExecutable, intrinsic);
-        executable->finishCreation(vm);
+        executable->finishCreation(vm, topLevelExecutable);
         return executable;
     }
     static FunctionExecutable* fromGlobalCode(
@@ -282,8 +282,16 @@ public:
     }
 
     // Cached poly proto structure for the result of constructing this executable.
-    Structure* cachedPolyProtoStructure() { return m_cachedPolyProtoStructure.get(); }
-    void setCachedPolyProtoStructure(VM& vm, Structure* structure) { m_cachedPolyProtoStructure.set(vm, this, structure); }
+    Structure* cachedPolyProtoStructure()
+    {
+        if (UNLIKELY(m_rareData))
+            return m_rareData->m_cachedPolyProtoStructure.get();
+        return nullptr;
+    }
+    void setCachedPolyProtoStructure(VM& vm, Structure* structure)
+    {
+        ensureRareData().m_cachedPolyProtoStructure.set(vm, this, structure);
+    }
 
     InlineWatchpointSet& ensurePolyProtoWatchpoint()
     {
@@ -294,11 +302,15 @@ public:
 
     Box<InlineWatchpointSet> sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
 
+    ScriptExecutable* topLevelExecutable() const { return m_topLevelExecutable.get(); }
+
+    TemplateObjectMap& ensureTemplateObjectMap(VM&);
+
 private:
     friend class ExecutableBase;
     FunctionExecutable(VM&, const SourceCode&, UnlinkedFunctionExecutable*, Intrinsic);
     
-    void finishCreation(VM&);
+    void finishCreation(VM&, ScriptExecutable* topLevelExecutable);
 
     friend class ScriptExecutable;
 
@@ -311,6 +323,8 @@ private:
         unsigned m_parametersStartOffset { 0 };
         unsigned m_typeProfilingStartOffset { UINT_MAX };
         unsigned m_typeProfilingEndOffset { UINT_MAX };
+        std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
+        WriteBarrier<Structure> m_cachedPolyProtoStructure;
     };
 
     RareData& ensureRareData()
@@ -321,7 +335,11 @@ private:
     }
     RareData& ensureRareDataSlow();
 
+    // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent.
+    // If RareData is required, materialize RareData, swap it, and store top-level executable pointer inside RareData.
+    // https://bugs.webkit.org/show_bug.cgi?id=197625
     std::unique_ptr<RareData> m_rareData;
+    WriteBarrier<ScriptExecutable> m_topLevelExecutable;
     WriteBarrier<UnlinkedFunctionExecutable> m_unlinkedExecutable;
     WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForCall;
     WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForConstruct;
@@ -329,7 +347,6 @@ private:
         WriteBarrier<InferredValue> m_singletonFunction;
         WatchpointState m_singletonFunctionState;
     };
-    WriteBarrier<Structure> m_cachedPolyProtoStructure;
     Box<InlineWatchpointSet> m_polyProtoWatchpoint;
 };
 
index c55d0d1..5005759 100644 (file)
@@ -200,7 +200,7 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
                     unlinkedFunctionExecutable->typeProfilingStartOffset(),
                     unlinkedFunctionExecutable->typeProfilingEndOffset());
             }
-            JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable->source()), moduleEnvironment);
+            JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable, moduleProgramExecutable->source()), moduleEnvironment);
             bool putResult = false;
             symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, unlinkedFunctionExecutable->name(), function, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true, putResult);
             RETURN_IF_EXCEPTION(scope, void());
index 2a4a407..99d6a98 100644 (file)
@@ -36,15 +36,16 @@ namespace JSC {
 const ClassInfo JSTemplateObjectDescriptor::s_info = { "TemplateObjectDescriptor", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSTemplateObjectDescriptor) };
 
 
-JSTemplateObjectDescriptor::JSTemplateObjectDescriptor(VM& vm, Ref<TemplateObjectDescriptor>&& descriptor)
+JSTemplateObjectDescriptor::JSTemplateObjectDescriptor(VM& vm, Ref<TemplateObjectDescriptor>&& descriptor, int endOffset)
     : Base(vm, vm.templateObjectDescriptorStructure.get())
     , m_descriptor(WTFMove(descriptor))
+    , m_endOffset(endOffset)
 {
 }
 
-JSTemplateObjectDescriptor* JSTemplateObjectDescriptor::create(VM& vm, Ref<TemplateObjectDescriptor>&& descriptor)
+JSTemplateObjectDescriptor* JSTemplateObjectDescriptor::create(VM& vm, Ref<TemplateObjectDescriptor>&& descriptor, int endOffset)
 {
-    JSTemplateObjectDescriptor* result = new (NotNull, allocateCell<JSTemplateObjectDescriptor>(vm.heap)) JSTemplateObjectDescriptor(vm, WTFMove(descriptor));
+    JSTemplateObjectDescriptor* result = new (NotNull, allocateCell<JSTemplateObjectDescriptor>(vm.heap)) JSTemplateObjectDescriptor(vm, WTFMove(descriptor), endOffset);
     result->finishCreation(vm);
     return result;
 }
index 75208bc..d719718 100644 (file)
@@ -38,7 +38,7 @@ public:
     static const bool needsDestruction = true;
     DECLARE_INFO;
 
-    static JSTemplateObjectDescriptor* create(VM&, Ref<TemplateObjectDescriptor>&&);
+    static JSTemplateObjectDescriptor* create(VM&, Ref<TemplateObjectDescriptor>&&, int);
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
@@ -49,13 +49,16 @@ public:
 
     JSArray* createTemplateObject(ExecState*);
 
+    int endOffset() const { return m_endOffset; }
+
 protected:
     static void destroy(JSCell*);
 
 private:
-    JSTemplateObjectDescriptor(VM&, Ref<TemplateObjectDescriptor>&&);
+    JSTemplateObjectDescriptor(VM&, Ref<TemplateObjectDescriptor>&&, int);
 
     Ref<TemplateObjectDescriptor> m_descriptor;
+    int m_endOffset { 0 };
 };
 
 } // namespace JSC
index 4373b64..4af8c24 100644 (file)
@@ -85,6 +85,11 @@ void ModuleProgramExecutable::destroy(JSCell* cell)
     static_cast<ModuleProgramExecutable*>(cell)->ModuleProgramExecutable::~ModuleProgramExecutable();
 }
 
+auto ModuleProgramExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
+{
+    return ensureTemplateObjectMapImpl(m_templateObjectMap);
+}
+
 void ModuleProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     ModuleProgramExecutable* thisObject = jsCast<ModuleProgramExecutable*>(cell);
@@ -93,6 +98,11 @@ void ModuleProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_unlinkedModuleProgramCodeBlock);
     visitor.append(thisObject->m_moduleEnvironmentSymbolTable);
     visitor.append(thisObject->m_moduleProgramCodeBlock);
+    if (TemplateObjectMap* map = thisObject->m_templateObjectMap.get()) {
+        auto locker = holdLock(thisObject->cellLock());
+        for (auto& entry : *map)
+            visitor.append(entry.value);
+    }
 }
 
 } // namespace JSC
index 6d355b7..f9b7e2a 100644 (file)
@@ -69,6 +69,8 @@ public:
 
     SymbolTable* moduleEnvironmentSymbolTable() { return m_moduleEnvironmentSymbolTable.get(); }
 
+    TemplateObjectMap& ensureTemplateObjectMap(VM&);
+
 private:
     friend class ExecutableBase;
     friend class ScriptExecutable;
@@ -80,6 +82,7 @@ private:
     WriteBarrier<UnlinkedModuleProgramCodeBlock> m_unlinkedModuleProgramCodeBlock;
     WriteBarrier<SymbolTable> m_moduleEnvironmentSymbolTable;
     WriteBarrier<ExecutableToCodeBlockEdge> m_moduleProgramCodeBlock;
+    std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
 };
 
 } // namespace JSC
index 9105416..234db8e 100644 (file)
@@ -216,6 +216,11 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF
     return nullptr;
 }
 
+auto ProgramExecutable::ensureTemplateObjectMap(VM&) -> TemplateObjectMap&
+{
+    return ensureTemplateObjectMapImpl(m_templateObjectMap);
+}
+
 void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell);
@@ -223,6 +228,11 @@ void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     Base::visitChildren(thisObject, visitor);
     visitor.append(thisObject->m_unlinkedProgramCodeBlock);
     visitor.append(thisObject->m_programCodeBlock);
+    if (TemplateObjectMap* map = thisObject->m_templateObjectMap.get()) {
+        auto locker = holdLock(thisObject->cellLock());
+        for (auto& entry : *map)
+            visitor.append(entry.value);
+    }
 }
 
 } // namespace JSC
index 6051167..b968444 100644 (file)
@@ -73,6 +73,8 @@ public:
 
     ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); }
 
+    TemplateObjectMap& ensureTemplateObjectMap(VM&);
+
 private:
     friend class ExecutableBase;
     friend class ScriptExecutable;
@@ -83,6 +85,7 @@ private:
 
     WriteBarrier<UnlinkedProgramCodeBlock> m_unlinkedProgramCodeBlock;
     WriteBarrier<ExecutableToCodeBlockEdge> m_programCodeBlock;
+    std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
 };
 
 } // namespace JSC
index 56a5d48..237d7de 100644 (file)
@@ -34,6 +34,7 @@
 #include "IsoCellSetInlines.h"
 #include "JIT.h"
 #include "JSCInlines.h"
+#include "JSTemplateObjectDescriptor.h"
 #include "LLIntEntrypoint.h"
 #include "ModuleProgramCodeBlock.h"
 #include "Parser.h"
@@ -435,6 +436,61 @@ Exception* ScriptExecutable::prepareForExecutionImpl(
     return nullptr;
 }
 
+ScriptExecutable* ScriptExecutable::topLevelExecutable()
+{
+    switch (type()) {
+    case FunctionExecutableType:
+        return jsCast<FunctionExecutable*>(this)->topLevelExecutable();
+    default:
+        return this;
+    }
+}
+
+JSArray* ScriptExecutable::createTemplateObject(ExecState* exec, JSTemplateObjectDescriptor* descriptor)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    TemplateObjectMap& templateObjectMap = ensureTemplateObjectMap(vm);
+    TemplateObjectMap::AddResult result;
+    {
+        auto locker = holdLock(cellLock());
+        result = templateObjectMap.add(descriptor->endOffset(), WriteBarrier<JSArray>());
+    }
+    if (JSArray* array = result.iterator->value.get())
+        return array;
+    JSArray* templateObject = descriptor->createTemplateObject(exec);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    result.iterator->value.set(vm, this, templateObject);
+    return templateObject;
+}
+
+auto ScriptExecutable::ensureTemplateObjectMapImpl(std::unique_ptr<TemplateObjectMap>& dest) -> TemplateObjectMap&
+{
+    if (dest)
+        return *dest;
+    auto result = std::make_unique<TemplateObjectMap>();
+    WTF::storeStoreFence();
+    dest = WTFMove(result);
+    return *dest;
+}
+
+auto ScriptExecutable::ensureTemplateObjectMap(VM& vm) -> TemplateObjectMap&
+{
+    switch (type()) {
+    case FunctionExecutableType:
+        return static_cast<FunctionExecutable*>(this)->ensureTemplateObjectMap(vm);
+    case EvalExecutableType:
+        return static_cast<EvalExecutable*>(this)->ensureTemplateObjectMap(vm);
+    case ProgramExecutableType:
+        return static_cast<ProgramExecutable*>(this)->ensureTemplateObjectMap(vm);
+    case ModuleProgramExecutableType:
+    default:
+        ASSERT(type() == ModuleProgramExecutableType);
+        return static_cast<ModuleProgramExecutable*>(this)->ensureTemplateObjectMap(vm);
+    }
+}
+
 CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const
 {
     return CodeBlockHash(source(), kind);
index 6c3eecc..cc5eb9d 100644 (file)
@@ -29,6 +29,8 @@
 
 namespace JSC {
 
+class JSArray;
+class JSTemplateObjectDescriptor;
 class IsoCellSet;
 
 class ScriptExecutable : public ExecutableBase {
@@ -37,6 +39,8 @@ public:
     static const unsigned StructureFlags = Base::StructureFlags;
 
     static void destroy(JSCell*);
+
+    using TemplateObjectMap = HashMap<uint64_t, WriteBarrier<JSArray>, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>;
         
     CodeBlockHash hashFor(CodeSpecializationKind) const;
 
@@ -112,12 +116,17 @@ public:
     template <typename ExecutableType>
     Exception* prepareForExecution(VM&, JSFunction*, JSScope*, CodeSpecializationKind, CodeBlock*& resultCodeBlock);
 
+    ScriptExecutable* topLevelExecutable();
+    JSArray* createTemplateObject(ExecState*, JSTemplateObjectDescriptor*);
+
 private:
     friend class ExecutableBase;
     Exception* prepareForExecutionImpl(VM&, JSFunction*, JSScope*, CodeSpecializationKind, CodeBlock*&);
 
     bool hasClearableCode(VM&) const;
 
+    TemplateObjectMap& ensureTemplateObjectMap(VM&);
+
 protected:
     ScriptExecutable(Structure*, VM&, const SourceCode&, bool isInStrictContext, DerivedContextType, bool isInArrowFunctionContext, EvalContextType, Intrinsic);
 
@@ -137,6 +146,8 @@ protected:
         m_hasCapturedVariables = hasCapturedVariables;
     }
 
+    static TemplateObjectMap& ensureTemplateObjectMapImpl(std::unique_ptr<TemplateObjectMap>& dest);
+
     SourceCode m_source;
     Intrinsic m_intrinsic { NoIntrinsic };
     bool m_didTryToEnterInLoop { false };
index d624a70..1458d04 100644 (file)
@@ -1846,7 +1846,7 @@ static EncodedJSValue JSC_HOST_CALL functionCreateBuiltin(ExecState* exec)
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     const SourceCode& source = makeSource(functionText, { });
-    JSFunction* func = JSFunction::create(vm, createBuiltinExecutable(vm, source, Identifier::fromString(&vm, "foo"), ConstructorKind::None, ConstructAbility::CannotConstruct)->link(vm, source), exec->lexicalGlobalObject());
+    JSFunction* func = JSFunction::create(vm, createBuiltinExecutable(vm, source, Identifier::fromString(&vm, "foo"), ConstructorKind::None, ConstructAbility::CannotConstruct)->link(vm, nullptr, source), exec->lexicalGlobalObject());
 
     return JSValue::encode(func);
 }
@@ -2087,6 +2087,15 @@ static EncodedJSValue JSC_HOST_CALL functionDisableDebuggerModeWhenIdle(ExecStat
     return changeDebuggerModeWhenIdle(exec, { });
 }
 
+static EncodedJSValue JSC_HOST_CALL functionDeleteAllCodeWhenIdle(ExecState* exec)
+{
+    VM* vm = &exec->vm();
+    vm->whenIdle([=] () {
+        vm->deleteAllCode(PreventCollectionAndDeleteAllCode);
+    });
+    return JSValue::encode(jsUndefined());
+}
+
 static EncodedJSValue JSC_HOST_CALL functionGlobalObjectCount(ExecState* exec)
 {
     return JSValue::encode(jsNumber(exec->vm().heap.globalObjectCount()));
@@ -2276,6 +2285,8 @@ void JSDollarVM::finishCreation(VM& vm)
     addFunction(vm, "enableDebuggerModeWhenIdle", functionEnableDebuggerModeWhenIdle, 0);
     addFunction(vm, "disableDebuggerModeWhenIdle", functionDisableDebuggerModeWhenIdle, 0);
 
+    addFunction(vm, "deleteAllCodeWhenIdle", functionDeleteAllCodeWhenIdle, 0);
+
     addFunction(vm, "globalObjectCount", functionGlobalObjectCount, 0);
     addFunction(vm, "globalObjectForObject", functionGlobalObjectForObject, 1);