[JSC] Add support for public class fields
authorcaitp@igalia.com <caitp@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Jan 2020 00:09:50 +0000 (00:09 +0000)
committercaitp@igalia.com <caitp@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Jan 2020 00:09:50 +0000 (00:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174212

Reviewed by Yusuke Suzuki.

JSTests:

New syntax invalidates some test expectations:

"async <linefeed> MethodDefinition" is no longer an unexpected "async"
token. It is now an instance field named "async" with no initializer,
and an automatic semicolon, followed by MethodDefinition.

"get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
being valid class field names.

Many class-syntax tests relating to automatic semicolon insertion are
no longer valid, as a line containing nothing but an identifier is now
a valid class element.

* stress/async-await-syntax.js:
* stress/class-fields-bytecode-cache.js: Added.
* stress/class-fields-computed-to-property-key.js: Added.
* stress/class-fields-function-name.js: Added.
* stress/class-fields-harmony.js: Added.
* stress/class-fields-proxy-define-property.js: Added.
* stress/class-fields-stress-instance.js: Added.
* stress/generator-syntax.js:
* stress/method-name.js:
* test262/config.yaml:

Source/JavaScriptCore:

Implements the instance class fields proposal (https://tc39.es/proposal-class-fields/),
minus support for private fields (split into a separate patch).

In summary, class fields are initialized by a synthetic JSFunction. In its unlinked state,
the UnlinkedFunctionExecutable for the function includes an ordered list of JSTokenLocations
pointing to the start of each class field in the class. Each of these fields are parsed and
included as DefineFieldNodes, which implement the appropriate DefineField behaviour in the
proposal. This synthetic function is only created, and only loaded, if there are class fields
present. The decision to use a synthetic function was for simplicity. There are a number of
factors which make inlining the initialization complicated, though we may opt to do this in
the future. For reference, the complexities are: instance fields and constructor in different
currently in different parsing arenas, distinct scopes between the 2 which require work to manage,
and complexity in doing to this work for child classes, where the location of initialization can
depend, and in some cases occur more than once.

Computed property fields require a new bytecode, op_to_property_key, as an implementation
detail. It is necessary in the proposal to convert computed properties to property keys
during class evaluation, rather than during field initialization. Additionally, we allocate
the class lexical scope when computed class fields are used (previously, only when there was
a class name), as a location to keep the computed property keys. They can be loaded from the
scope via indexed keys.

To illustrate computed field names in action, consider the following pseudocode:

    <during class evaluation>
    1) fieldName = emitNode({expr})
    2) fieldName = emitToPropertyKey(fieldName)
    3) classScope[numComputedNames++] = fieldName

    <during class field initialization>
    1) fieldName = emitGetFromScope(classScope, computedFieldNameIndex++)
    2) value = emitNode({initializer})
    3) instance[fieldName] = value

The feature is currently hidden behind the feature flag JSC::Options::useClassFields.

LayoutTests:

New syntax invalidates some test expectations:

"async <linefeed> MethodDefinition" is no longer an unexpected "async"
token. It is now an instance field named "async" with no initializer,
and an automatic semicolon, followed by MethodDefinition.

"get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
being valid class field names.

Many class-syntax tests relating to automatic semicolon insertion are
no longer valid, as a line containing nothing but an identifier is now
a valid class element.

* js/class-syntax-semicolon-expected.txt:
* js/script-tests/class-syntax-semicolon.js:

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

63 files changed:
JSTests/ChangeLog
JSTests/stress/async-await-syntax.js
JSTests/stress/class-fields-bytecode-cache.js [new file with mode: 0644]
JSTests/stress/class-fields-computed-to-property-key.js [new file with mode: 0644]
JSTests/stress/class-fields-function-name.js [new file with mode: 0644]
JSTests/stress/class-fields-harmony.js [new file with mode: 0644]
JSTests/stress/class-fields-proxy-define-property.js [new file with mode: 0644]
JSTests/stress/class-fields-stress-instance.js [new file with mode: 0644]
JSTests/stress/generator-syntax.js
JSTests/stress/method-name.js
JSTests/test262/config.yaml
LayoutTests/ChangeLog
LayoutTests/js/class-syntax-semicolon-expected.txt
LayoutTests/js/script-tests/class-syntax-semicolon.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp
Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Source/JavaScriptCore/builtins/BuiltinExecutables.h
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/bytecode/BytecodeList.rb
Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp
Source/JavaScriptCore/bytecode/ExecutableInfo.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserModes.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/CachedTypes.cpp
Source/JavaScriptCore/runtime/CodeCache.cpp
Source/JavaScriptCore/runtime/CodeCache.h
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/DirectEvalExecutable.cpp
Source/JavaScriptCore/runtime/DirectEvalExecutable.h
Source/JavaScriptCore/runtime/EvalExecutable.cpp
Source/JavaScriptCore/runtime/EvalExecutable.h
Source/JavaScriptCore/runtime/FunctionPrototype.cpp
Source/JavaScriptCore/runtime/IndirectEvalExecutable.cpp
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/ModuleProgramExecutable.h
Source/JavaScriptCore/runtime/OptionsList.h
Source/JavaScriptCore/runtime/ProgramExecutable.h

index 7c8d62d..156aa62 100644 (file)
@@ -1,3 +1,34 @@
+2020-01-15  Caitlin Potter <caitp@igalia.com>
+
+        [JSC] Add support for public class fields
+        https://bugs.webkit.org/show_bug.cgi?id=174212
+
+        Reviewed by Yusuke Suzuki.
+
+        New syntax invalidates some test expectations:
+
+        "async <linefeed> MethodDefinition" is no longer an unexpected "async"
+        token. It is now an instance field named "async" with no initializer,
+        and an automatic semicolon, followed by MethodDefinition.
+
+        "get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
+        being valid class field names.
+
+        Many class-syntax tests relating to automatic semicolon insertion are
+        no longer valid, as a line containing nothing but an identifier is now
+        a valid class element.
+
+        * stress/async-await-syntax.js:
+        * stress/class-fields-bytecode-cache.js: Added.
+        * stress/class-fields-computed-to-property-key.js: Added.
+        * stress/class-fields-function-name.js: Added.
+        * stress/class-fields-harmony.js: Added.
+        * stress/class-fields-proxy-define-property.js: Added.
+        * stress/class-fields-stress-instance.js: Added.
+        * stress/generator-syntax.js:
+        * stress/method-name.js:
+        * test262/config.yaml:
+
 2020-01-15  Keith Miller  <keith_miller@apple.com>
 
         Revert bytecode checkpoints since it breaks watch
index 0bdbd1b..28301a4 100644 (file)
@@ -560,9 +560,6 @@ async function fn(b) {
         // ObjectLiteral AsyncMethodDefinition
         { prefix: "({ async", suffix: "method() {} }).method" },
 
-        // ClassLiteral AsyncMethodDefinition
-        { prefix: "(class { async", suffix: "method() {} }).prototype.method" },
-
         // AsyncArrowFunctions
         { prefix: "(async", suffix: "param => 1)" },
         { prefix: "(async", suffix: "(param) => 1)" },
@@ -578,6 +575,18 @@ async function fn(b) {
         testLineFeedErrors(prefix, suffix);
     }
 
+    let testsClass = [
+        // ClassLiteral AsyncMethodDefinition
+        { prefix: "(class { async", suffix: "method() {} }).prototype.method" },
+    ];
+
+    for (let { prefix, suffix } of testsClass) {
+        testSyntax(`${prefix} ${suffix}`);
+        testSyntax(`"use strict";${prefix} ${suffix}`);
+        shouldBe("function", typeof eval(`${prefix} ${suffix}`));
+        shouldBe("function", typeof eval(`"use strict";${prefix} ${suffix}`));
+    }
+
     // AsyncFunctionDeclaration
     testSyntax("async function foo() {}");
     testLineFeeds("async", "function foo() {}");
diff --git a/JSTests/stress/class-fields-bytecode-cache.js b/JSTests/stress/class-fields-bytecode-cache.js
new file mode 100644 (file)
index 0000000..1d4fcfa
--- /dev/null
@@ -0,0 +1,19 @@
+//@ runBytecodeCache("--useClassFields=true")
+
+function assert(a, e) {
+    if (a !== e)
+        throw new Erro("Expected: " + e + " but got: " + a);
+}
+
+class C{
+    foo() {
+        return this.f;
+    }
+
+    f = 15;
+}
+
+let c = new C();
+let r = c.foo();
+assert(r, 15);
+
diff --git a/JSTests/stress/class-fields-computed-to-property-key.js b/JSTests/stress/class-fields-computed-to-property-key.js
new file mode 100644 (file)
index 0000000..710555b
--- /dev/null
@@ -0,0 +1,131 @@
+//@ requireOptions("--useClassFields=1")
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+function shallowEquals(a, b) {
+    if (Array.isArray(a) !== Array.isArray(b))
+        return false;
+    if (Array.isArray(a)) {
+        let aLen = a.length | 0;
+        let bLen = a.length | 0;
+        if (aLen === bLen) {
+            for (let i = 0; i < aLen; ++i) {
+                if (a[i] !== b[i])
+                    return false;
+            }
+            return true;
+        }
+    }
+    return a === b;
+}
+
+assert.same = function(a, b, message) {
+    let msg = message ? `. ${message}` : "";
+    assert(shallowEquals(a, b), `Expected ${a} to equal ${b}${msg}`);
+}
+
+assert.not = function(a, message) {
+    assert(!a, message);
+}
+
+function makeComputedKey(options = {}) {
+    let { toString, valueOf, toPrimitive } = options;
+    let hasToPrimitive = options.hasOwnProperty("toPrimitive");
+    function toPrimitiveFn() {
+        order.push(`call @@toPrimitive() -> ${toPrimitive}`);
+        return toPrimitive;
+    }
+
+    function toStringFn() {
+        order.push(`call toString() -> ${toString}`);
+        return toString;
+    }
+
+    function valueOfFn() {
+        order.push(`call valueOf() -> ${valueOf}`);
+        return valueOf;
+    }
+
+    return {
+        get [Symbol.toPrimitive]() {
+            order.push(`load @@toPrimitive`);
+            this.toPrimitiveLoaded++;
+            return hasToPrimitive ? toPrimitiveFn : void 0;
+        },
+        get toString() {
+            order.push(`load toString`);
+            return toStringFn;
+        },
+        get valueOf() {
+            order.push(`load valueOf`);
+            return valueOfFn;
+        },
+    };
+}
+
+let object = { toString() { return "object"; } };
+let order = [];
+class Base {
+    constructor() {
+        order.push("construct Base");
+        return new Proxy(this, {
+            defineProperty(target, p, desc) {
+                order.push(`defineProperty ${p}`);
+                return Reflect.defineProperty(target, p, desc);
+            }
+        });
+    }
+}
+
+let computedKey = makeComputedKey({ toString: "test" });
+assert.same(order, []);
+class BasicTPK extends Base {
+    [computedKey] = "basic";
+}
+
+assert.same(order, ["load @@toPrimitive", "load toString", "call toString() -> test"]);
+order.length = 0;
+let instance = new BasicTPK;
+assert.same(order, ["construct Base", "defineProperty test"]);
+assert.same(instance.test, "basic");
+
+// The computed key is evaluated during class evaluation, not initialization
+order.length = 0;
+computedKey = makeComputedKey({ toString: object, valueOf: null });
+instance = new BasicTPK;
+assert.same(instance.test, "basic");
+assert.not(instance.hasOwnProperty("null"));
+assert.same(order, ["construct Base", "defineProperty test"]);
+
+order.length = 0;
+class BasicTPK2 extends Base {
+    [computedKey] = "valueOf";
+}
+instance = new BasicTPK2;
+assert.not(instance.hasOwnProperty("test"));
+assert.same(instance.null, "valueOf");
+assert.same(order, [
+    "load @@toPrimitive",
+    "load toString",
+    "call toString() -> object",
+    "load valueOf",
+    "call valueOf() -> null",
+    "construct Base",
+    "defineProperty null"
+]);
+
+// exoticToPrimitive
+order.length = 0;
+computedKey = makeComputedKey({ toPrimitive: "bingo" });
+class ExoticTPK {
+    [computedKey] = "exotic";
+}
+
+assert.same(order, ["load @@toPrimitive", "call @@toPrimitive() -> bingo"]);
+order.length = 0;
+instance = new ExoticTPK;
+assert.same(order, ["construct Base", "defineProperty bingo"]);
+assert.same(instance.bingo, "exotic");
diff --git a/JSTests/stress/class-fields-function-name.js b/JSTests/stress/class-fields-function-name.js
new file mode 100644 (file)
index 0000000..c229a61
--- /dev/null
@@ -0,0 +1,17 @@
+//@ requireOptions("--useClassFields=true")
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+class A {
+    foo = function() { };
+    bar = class {};
+    baz = "test";
+}
+
+var a = new A();
+assert(a.foo.name == "foo");
+assert(a.bar.name == "bar");
+assert(a.baz.name == undefined);
diff --git a/JSTests/stress/class-fields-harmony.js b/JSTests/stress/class-fields-harmony.js
new file mode 100644 (file)
index 0000000..165424b
--- /dev/null
@@ -0,0 +1,908 @@
+//@ requireOptions("--useClassFields=1")
+//@ defaultNoEagerRun
+
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Copyright 2019 Igalia S.L.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"use strict";
+
+function classOf(object) {
+   // Argument must not be null or undefined.
+   var string = Object.prototype.toString.call(object);
+   // String has format [object <ClassName>].
+   return string.substring(8, string.length - 1);
+}
+
+/**
+ * Compares two objects for key/value equality.
+ * Returns true if they are equal, false otherwise.
+ */
+function deepObjectEquals(a, b) {
+  var aProps = Object.keys(a);
+  aProps.sort();
+  var bProps = Object.keys(b);
+  bProps.sort();
+  if (!deepEquals(aProps, bProps)) {
+    return false;
+  }
+  for (var i = 0; i < aProps.length; i++) {
+    if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Compares two JavaScript values for type and value equality.
+ * It checks internals of arrays and objects.
+ */
+function deepEquals(a, b) {
+  if (a === b) {
+    // Check for -0.
+    if (a === 0) return (1 / a) === (1 / b);
+    return true;
+  }
+  if (typeof a != typeof b) return false;
+  if (typeof a == 'number') return isNaN(a) && isNaN(b);
+  if (typeof a !== 'object' && typeof a !== 'function') return false;
+  // Neither a nor b is primitive.
+  var objectClass = classOf(a);
+  if (objectClass !== classOf(b)) return false;
+  if (objectClass === 'RegExp') {
+    // For RegExp, just compare pattern and flags using its toString.
+    return (a.toString() === b.toString());
+  }
+  // Functions are only identical to themselves.
+  if (objectClass === 'Function') return false;
+  if (objectClass === 'Array') {
+    var elementCount = 0;
+    if (a.length != b.length) {
+      return false;
+    }
+    for (var i = 0; i < a.length; i++) {
+      if (!deepEquals(a[i], b[i])) return false;
+    }
+    return true;
+  }
+  if (objectClass == 'String' || objectClass == 'Number' ||
+      objectClass == 'Boolean' || objectClass == 'Date') {
+    if (a.valueOf() !== b.valueOf()) return false;
+  }
+  return deepObjectEquals(a, b);
+}
+
+/**
+ * Throws an exception containing the user_message (if any) and the values.
+ */
+function fail(expected, found, user_message = '') {
+  // TODO(cira): Replace String with PrettyPrint for objects and arrays.
+  var message = 'Failure' + (user_message ? ' (' + user_message + ')' : '') +
+      ': expected <' + String(expected) + '>, found <' + String(found) + '>.';
+  throw new Error(message);
+}
+
+/**
+ * Throws if obj is not of given type.
+ */
+function assertInstanceof(obj, type) {
+  if (!(obj instanceof type)) {
+    var actualTypeName = null;
+    var actualConstructor = Object.getPrototypeOf(obj).constructor;
+    if (typeof actualConstructor == "function") {
+      actualTypeName = actualConstructor.name || String(actualConstructor);
+    }
+    throw new Error('Object <' + obj + '> is not an instance of <' +
+                    (type.name || type) + '>' +
+                    (actualTypeName ? ' but of < ' + actualTypeName + '>' : ''));
+  }
+}
+
+/**
+ * Throws if obj is not the same as expected.
+ */
+function assertSame(expected, found, name_opt) {
+  if (found === expected) {
+    if (expected !== 0 || (1 / expected) === (1 / found))
+      return;
+  } else if ((expected !== expected) && (found !== found)) {
+      return;
+  }
+  fail(expected, found, name_opt);
+}
+
+/**
+ * Throws if two variables have different types or values.
+ */
+
+function assertEquals(expected, found, user_message = '') {
+  if (!deepEquals(expected, found)) {
+    fail(expected, found, user_message);
+  }
+}
+
+/**
+ * Throws if two variables have equal types or values.
+ */
+
+function assertNotEquals(expected, found, user_message = '') {
+  if (deepEquals(expected, found)) {
+    fail(expected, found, user_message);
+  }
+}
+
+/**
+ * Throws if value is false.
+ */
+function assertTrue(value, user_message = '') {
+  assertEquals(true, value, user_message);
+}
+
+
+/**
+ * Throws if value is true.
+ */
+function assertFalse(value, user_message = '') {
+  assertEquals(false, value, user_message);
+}
+
+/**
+ * Runs code() and asserts that it throws the specified exception.
+ */
+function assertThrows(code, type_opt, cause_opt) {
+  try {
+    if (typeof code == 'function') {
+      code();
+    } else {
+      eval(code);
+    }
+  } catch (e) {
+    if (typeof type_opt == 'function') {
+      assertInstanceof(e, type_opt);
+    }
+    if (arguments.length >= 3) {
+      assertEquals(cause_opt, e.type, 'thrown exception type mismatch');
+    }
+    // Success.
+    return;
+  }
+  var expected = arguments.length >= 3 ? cause_opt :
+      typeof type_opt == 'function' ? type_opt : 'any exception';
+  fail(expected, 'no exception', 'expected thrown exception');
+}
+
+{
+  class C {
+    a;
+  }
+
+  assertEquals(undefined, C.a);
+
+  let c = new C;
+  let descriptor = Object.getOwnPropertyDescriptor(c, 'a');
+  assertTrue(c.hasOwnProperty('a'));
+  assertTrue(descriptor.writable);
+  assertTrue(descriptor.enumerable);
+  assertTrue(descriptor.configurable);
+  assertEquals(undefined, c.a);
+}
+
+{
+  class C {
+    x = 1;
+    constructor() {}
+  }
+
+  let c = new C;
+  assertEquals(1, c.x);
+}
+
+{
+  function t() {
+    class X {
+      x = 1;
+      constructor() {}
+    }
+
+    var x = new X;
+    return x.x;
+  }
+
+  assertEquals(1, t());
+}
+
+{
+  let x = 'a';
+  class C {
+    a;
+    b = x;
+    c = 1;
+    hasOwnProperty() { return 1;}
+  }
+
+  let c = new C;
+  assertEquals(undefined, c.a);
+  assertEquals('a', c.b);
+  assertEquals(1, c.c);
+  assertEquals(undefined, c.d);
+  assertEquals(1, c.hasOwnProperty());
+}
+
+{
+  class C {
+    x = Object.freeze(this);
+    c = 42;
+  }
+  assertThrows(() => { new C; }, TypeError);
+}
+
+{
+  class C {
+    c = this;
+    d = () => this;
+  }
+
+  let c = new C;
+  assertEquals(c, c.c);
+  assertEquals(c, c.d());
+
+  assertEquals(undefined, C.c);
+  assertEquals(undefined, C.d);
+}
+
+{
+  class C {
+    c = 1;
+    d = this.c;
+  }
+
+  let c = new C;
+  assertEquals(1, c.c);
+  assertEquals(1, c.d);
+
+  assertEquals(undefined, C.c);
+  assertEquals(undefined, C.d);
+}
+
+{
+  class C {
+    b = 1;
+    c = () => this.b;
+  }
+
+  let c = new C;
+  assertEquals(1, c.b);
+  assertEquals(1, c.c());
+
+  assertEquals(undefined, C.c);
+  assertEquals(undefined, C.b);
+}
+
+{
+  let x = 'a';
+  class C {
+    b = 1;
+    c = () => this.b;
+    e = () => x;
+  }
+
+  let c = new C;
+  assertEquals(1, c.b);
+  assertEquals('a', c.e());
+
+  let a = {b : 2 };
+  assertEquals(1, c.c.call(a));
+
+  assertEquals(undefined, C.b);
+  assertEquals(undefined, C.c);
+}
+
+{
+  let x = 'a';
+  class C {
+    c = 1;
+    d = function() { return this.c; };
+    e = function() { return x; };
+  }
+
+  let c = new C;
+  assertEquals(1, c.c);
+  assertEquals(1, c.d());
+  assertEquals('a', c.e());
+
+  c.c = 2;
+  assertEquals(2, c.d());
+
+  let a = {c : 3 };
+  assertEquals(3, c.d.call(a));
+
+  assertThrows(c.d.bind(undefined));
+
+  assertEquals(undefined, C.c);
+  assertEquals(undefined, C.d);
+  assertEquals(undefined, C.e);
+}
+
+{
+  class C {
+    c = function() { return 1 };
+  }
+
+  let c = new C;
+  assertEquals('c', c.c.name);
+}
+
+{
+  let d = function() { return new.target; }
+  class C {
+    c = d;
+  }
+
+  let c = new C;
+  assertEquals(undefined, c.c());
+  assertEquals(new d, new c.c());
+}
+
+{
+  class C {
+    c = () => new.target;
+  }
+
+  let c = new C;
+  assertEquals(undefined, c.c());
+}
+
+{
+  let run = false;
+  class C {
+    c = () => {
+      let b;
+      class A {
+        constructor() {
+          b = new.target;
+        }
+      };
+      new A;
+      run = true;
+      assertEquals(A, b);
+    }
+  }
+
+  let c = new C;
+  c.c();
+  assertTrue(run);
+}
+
+{
+  class C {
+    c = new.target;
+  }
+
+  let c = new C;
+  assertEquals(undefined, c.c);
+}
+
+{
+  class B {
+    c = 1;
+  }
+
+  class C extends B {}
+
+  let c = new C;
+  assertEquals(1, c.c);
+}
+
+{
+  assertThrows(() => {
+    class C {
+      c = new C;
+    }
+    let c = new C;
+  });
+}
+
+(function test() {
+  function makeC() {
+    var x = 1;
+
+    return class {
+      a = () => () => x;
+    }
+  }
+
+  let C = makeC();
+  let c = new C;
+  let f = c.a();
+  assertEquals(1, f());
+})()
+
+{
+  let c1 = "c";
+  class C {
+    ["a"] = 1;
+    ["b"];
+    [c1];
+  }
+
+  let c = new C;
+  assertEquals(1, c.a);
+  assertEquals(undefined, c.b);
+  assertEquals(undefined, c[c1]);
+}
+
+{
+  let log = [];
+  function run(i) {
+    log.push(i);
+    return i;
+  }
+
+  class C {
+    [run(1)] = run(6);
+    [run(2)] = run(7);
+    [run(3)]() { run(9);}
+    [run(4)] = run(8);
+    [run(5)]() { throw new Error('should not execute');};
+  }
+
+  let c = new C;
+  c[3]();
+  assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
+}
+
+function x() {
+  // This tests lazy parsing.
+  return function() {
+    let log = [];
+    function run(i) {
+      log.push(i);
+      return i;
+    }
+
+    class C {
+      [run(1)] = run(6);
+      [run(2)] = run(7);
+      [run(3)]() { run(9);}
+      [run(4)] = run(8);
+      [run(5)]() { throw new Error('should not execute');};
+    }
+
+    let c = new C;
+    c[3]();
+    assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
+  }
+}
+x()();
+
+{
+  class C {}
+  class D {
+    [C];
+  }
+
+  let d = new D;
+  assertThrows(() => { class X { [X] } let x = new X;});
+  assertEquals(undefined, d[C]);
+}
+
+{
+  class B {
+    a = 1;
+  }
+
+  class C extends B {
+    b = 2;
+    constructor() {
+      super();
+    }
+  }
+
+  let c = new C;
+  assertEquals(1, c.a);
+  assertEquals(2, c.b);
+}
+
+{
+  var log = [];
+  function addToLog(item) { log.push(item); }
+
+  class B {
+    a = 1;
+    constructor() {
+      addToLog("base constructor");
+    }
+  }
+
+  function initF() {
+    addToLog("init f");
+    return 1;
+  }
+
+  class C extends B {
+    f = initF();
+
+    constructor() {
+      addToLog("derived constructor");
+      var t = () => {
+        addToLog("t");
+        if (1==-1) {
+          super();
+        } else {
+          super();
+        }
+      }
+      (() => {
+        addToLog("anon");
+        t();
+      })();
+    }
+  }
+
+  let c = new  C;
+  assertEquals(1, c.f);
+  assertEquals(1, c.a);
+  assertEquals(["derived constructor","anon","t","base constructor","init f"],
+               log);
+}
+
+{
+  class B {
+    a = 1;
+    returnA = () => this.a;
+  }
+
+  class C extends B {
+    c = this.a;
+    d = 2;
+    returnC = () => this.c;
+    returnD = () => this.d;
+  }
+
+  let c = new C;
+  assertEquals(1, c.a);
+  assertEquals(1, c.returnA());
+  assertEquals(1, c.c);
+  assertEquals(1, c.returnA());
+  assertEquals(1, c.returnC());
+  assertEquals(2, c.d);
+  assertEquals(2, c.returnD());
+
+  let c2 = new C;
+  assertNotEquals(c2.returnA, c.returnA);
+  assertNotEquals(c2.returnC, c.returnC);
+  assertNotEquals(c2.returnD, c.returnD);
+}
+
+{
+  let foo = undefined;
+  class B {
+    set d(x) {
+      foo = x;
+    }
+  }
+
+  class C extends B {
+    d = 2;
+  }
+
+  let c = new C;
+  assertEquals(undefined, foo);
+  assertEquals(2, c.d);
+}
+
+{
+  class B {}
+  class C extends B {
+    constructor() {
+      super();
+    }
+
+    c = 1;
+  }
+
+  let c = new C;
+  assertEquals(1, c.c);
+}
+
+{
+  class B {}
+  class C extends B {
+    constructor() {
+      let t = () => {
+          super();
+      }
+      t();
+    }
+
+    c = 1;
+  }
+
+  let c = new C;
+  assertEquals(1, c.c);
+}
+
+{
+  let log = [];
+
+  class B {}
+
+  class C extends B {
+
+    x = (log.push(1), 1);
+
+    constructor() {
+      let t = () => {
+        class D extends B {
+
+          x = (log.push(2), 2);
+
+          constructor() {
+            let p = () => {
+              super();
+            }
+
+            p();
+          }
+        }
+
+        let d = new D();
+        assertEquals(2, d.x);
+        super();
+      }
+
+      t();
+    }
+  }
+
+
+  let c = new C;
+  assertEquals(1, c.x);
+  assertEquals([2, 1], log);
+}
+
+{
+  let log = [];
+  class C1 extends class {} {
+    x = log.push(1);
+    constructor() {
+      var t = () => super();
+      super();
+      t();
+    }
+  }
+
+  assertThrows(() => new C1, ReferenceError);
+  assertEquals([1], log);
+
+  log = [];
+  class C2 extends class {} {
+    x = log.push(1);
+    constructor() {
+      var t = () => super();
+      t();
+      super();
+    }
+  }
+
+  assertThrows(() => new C2, ReferenceError);
+  assertEquals([1], log);
+}
+
+{
+  class C1 extends class {} {
+    x = 1
+    constructor() {
+      eval("super()");
+    }
+  }
+
+  let c = new C1;
+  assertEquals(1, c.x);
+
+  class C2 extends class {} {
+    x = 1
+    constructor() {
+      var t = () => {
+        eval("super()");
+      }
+      t();
+    }
+  }
+
+  c = new C2;
+  assertEquals(1, c.x);
+}
+
+{
+  class C {
+    ['x'] = 1;
+    ['y'] = 2;
+  }
+
+  class C1 extends C {
+    ['x'] = 3;
+    ['z'] = 4;
+  }
+
+  let c = new C1;
+  assertEquals(3, c.x);
+  assertEquals(2, c.y);
+  assertEquals(4, c.z);
+}
+
+{
+  class X extends class {} {
+    c = 1;
+
+    constructor() {
+      let t = () => {
+
+        class P extends class {} {
+          constructor() {
+            let t = () => { super(); };
+            t();
+          }
+        }
+
+        let p = new P;
+        assertEquals(undefined, p.c);
+        super();
+      }
+
+      t();
+    }
+  }
+
+  let x = new X;
+  assertEquals(1, x.c);
+}
+
+{
+  class A {
+    a() { return 1; }
+  }
+
+  class C extends A {
+    b = super.a();
+    c = () => super.a;
+    d = () => super.a();
+    e = super.a;
+    f = super.b;
+  }
+
+  let c = new C;
+  assertEquals(1, c.a());
+  assertEquals(1, c.b);
+  assertEquals(1, c.c()());
+  assertEquals(1, c.d());
+  assertEquals(1, c.e());
+  assertFalse(Object.hasOwnProperty(c, 'a'));
+  assertEquals(c.a, c.e);
+  assertEquals(undefined, c.f);
+}
+
+{
+  function t() {
+    return class {
+      ['x'] = 1;
+    }
+  }
+
+  let klass = t();
+  let obj = new klass;
+  assertEquals(1, obj.x);
+}
+
+{
+  new class {
+    t = 1;
+    constructor(t = this.t) {
+      assertEquals(1, t);
+    }
+  }
+
+  new class extends class {} {
+    t = 1;
+    constructor(t = (super(), this.t)) {
+      assertEquals(1, t);
+    }
+  }
+
+  assertThrows(() => {
+    new class extends class {} {
+      t = 1;
+      constructor(t = this.t) {
+        super();
+      }
+    }
+  }, ReferenceError);
+}
+
+{
+  class X {
+    p = function() { return arguments[0]; }
+  }
+
+  let x = new X;
+  assertEquals(1, x.p(1));
+}
+
+{
+  class X {
+    t = () => {
+      function p() { return arguments[0]; };
+      return p;
+    }
+  }
+
+  let x = new X;
+  let p = x.t();
+  assertEquals(1, p(1));
+}
+
+{
+  class X {
+    t = () => {
+      function p() { return eval("arguments[0]"); };
+      return p;
+    }
+  }
+
+  let x = new X;
+  let p = x.t();
+  assertEquals(1, p(1));
+}
+
+{
+  class X {
+    p = eval("(function() { return arguments[0]; })(1)");
+  }
+
+  let x = new X;
+  assertEquals(1, x.p);
+}
+
+{
+  let thisInInitializer, thisInConstructor, thisFromArrowFn, arrowFn;
+  let C = class extends class {} {
+    field = (thisInInitializer = this, thisFromArrowFn = arrowFn());
+    constructor() {
+      arrowFn = () => this;
+      super();
+      thisInConstructor = this;
+    }
+  };
+
+  let c = new C();
+
+  assertSame(thisInInitializer, c);
+  assertSame(thisFromArrowFn, c);
+  assertSame(thisInConstructor, c);
+}
+
diff --git a/JSTests/stress/class-fields-proxy-define-property.js b/JSTests/stress/class-fields-proxy-define-property.js
new file mode 100644 (file)
index 0000000..f07b6b3
--- /dev/null
@@ -0,0 +1,41 @@
+//@ requireOptions("--useClassFields=1")
+//@ defaultNoEagerRun
+
+function assert(a, e, m) {
+    m = m || "Expected: " + e + " but got: " + a;
+    if (a !== e)
+        throw new Error(m);
+}
+
+function assertArrayContent(a, e) {
+    assert(a.length, e.length, "Size of arrays doesn't match");
+    for (var i = 0; i < a.length; i++)
+        assert(a[i], e[i], "a[" + i + "] = " + a[i] + " but e[" + i + "] = " + e[i]);
+}
+
+let arr = [];
+
+class ProxyBase {
+    constructor() {
+        return new Proxy(this, {
+            defineProperty: function (target, key, descriptor) {
+                arr.push(key);
+                assert(descriptor.enumerable, true);
+                assert(descriptor.configurable, true);
+                assert(descriptor.writable, true);
+                return Reflect.defineProperty(target, key, descriptor);
+            }
+        });
+    }
+}
+
+class Test extends ProxyBase {
+  f = 3;
+  g = "test";
+}
+
+let t = new Test();
+assert(t.f, 3);
+assert(t.g, "test");
+
+assertArrayContent(arr, ["f", "g"]);
diff --git a/JSTests/stress/class-fields-stress-instance.js b/JSTests/stress/class-fields-stress-instance.js
new file mode 100644 (file)
index 0000000..56624fb
--- /dev/null
@@ -0,0 +1,17 @@
+//@ requireOptions("--useClassFields=true")
+
+class A {
+    a = 0;
+    b = 1;
+    e = function () { return 0; };
+    ["g"] = 0;
+    h = eval("true");
+}
+
+for (var i = 0; i < 10000; i++)
+    new A();
+
+fullGC();
+
+for (var i = 0; i < 10000; i++)
+    new A();
index 862c2a5..3214d64 100644 (file)
@@ -1,3 +1,5 @@
+//@ requireOptions("--useClassFields=1")
+
 function testSyntax(script) {
     try {
         eval(script);
@@ -26,7 +28,7 @@ class Hello {
     get *gen() {
     }
 }
-`, `SyntaxError: Unexpected token '*'. Expected an opening '(' before a method's parameter list.`);
+`, `SyntaxError: Unexpected token '*'. Expected a ';' following a class field.`);
 
 
 testSyntaxError(`
@@ -34,7 +36,7 @@ class Hello {
     set *gen(value) {
     }
 }
-`, `SyntaxError: Unexpected token '*'. Expected an opening '(' before a method's parameter list.`);
+`, `SyntaxError: Unexpected token '*'. Expected a ';' following a class field.`);
 
 testSyntaxError(`
 function ** gen() { }
index 45ee5ba..28a9d82 100644 (file)
@@ -1,3 +1,5 @@
+//@ requireOptions("--useClassFields=1")
+
 function testSyntax(script) {
     try {
         eval(script);
@@ -25,7 +27,7 @@ testSyntaxError(`
 class Hello {
     hello hello() { }
 }
-`, `SyntaxError: Unexpected identifier 'hello'. Expected an opening '(' before a method's parameter list.`);
+`, `SyntaxError: Unexpected identifier 'hello'. Expected a ';' following a class field.`);
 
 testSyntaxError(`
 let obj = {
index 0ae3a5b..2383581 100644 (file)
@@ -3,6 +3,7 @@
 flags:
   BigInt: useBigInt
   WeakRef: useWeakRefs
+  class-fields-public: useClassFields
 skip:
   features:
     - SharedArrayBuffer
@@ -10,7 +11,6 @@ skip:
     # https://bugs.webkit.org/show_bug.cgi?id=174931
     - regexp-lookbehind
 
-    - class-fields-public
     - class-fields-private
     - class-methods-private
     - class-static-fields-public
index 20f1509..997a493 100644 (file)
@@ -1,3 +1,26 @@
+2020-01-15  Caitlin Potter <caitp@igalia.com>
+
+        [JSC] Add support for public class fields
+        https://bugs.webkit.org/show_bug.cgi?id=174212
+
+        Reviewed by Yusuke Suzuki.
+
+        New syntax invalidates some test expectations:
+
+        "async <linefeed> MethodDefinition" is no longer an unexpected "async"
+        token. It is now an instance field named "async" with no initializer,
+        and an automatic semicolon, followed by MethodDefinition.
+
+        "get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
+        being valid class field names.
+
+        Many class-syntax tests relating to automatic semicolon insertion are
+        no longer valid, as a line containing nothing but an identifier is now
+        a valid class element.
+
+        * js/class-syntax-semicolon-expected.txt:
+        * js/script-tests/class-syntax-semicolon.js:
+
 2020-01-15  Chris Dumez  <cdumez@apple.com>
 
         Regression(r253213) Load hang and high CPU usage when trying to load myuhc.com
index 8d34286..af5adc6 100644 (file)
@@ -3,13 +3,8 @@ Tests for ES6 class syntax containing semicolon in the class body
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS class A { foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list..
 PASS class A { foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a method body..
-PASS class A { get ; foo() { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list..
-PASS class A { get foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for getter definition..
 PASS class A { get foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a getter body..
-PASS class A { set ; foo(x) { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list..
-PASS class A { set foo;(x) { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for setter definition..
 PASS class A { set foo(x) ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a setter body..
 PASS class A { ; } did not throw exception.
 PASS class A { foo() { } ; } did not throw exception.
index 86b3d56..160ee9e 100644 (file)
@@ -1,12 +1,7 @@
 description('Tests for ES6 class syntax containing semicolon in the class body');
 
-shouldThrow("class A { foo;() { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'");
 shouldThrow("class A { foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a method body.'");
-shouldThrow("class A { get ; foo() { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'");
-shouldThrow("class A { get foo;() { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for getter definition.'");
 shouldThrow("class A { get foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a getter body.'");
-shouldThrow("class A { set ; foo(x) { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'");
-shouldThrow("class A { set foo;(x) { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for setter definition.'");
 shouldThrow("class A { set foo(x) ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a setter body.'");
 
 shouldNotThrow("class A { ; }");
index 58e1a17..bbee9cf 100644 (file)
@@ -1,3 +1,46 @@
+2020-01-15  Caitlin Potter <caitp@igalia.com>
+
+        [JSC] Add support for public class fields
+        https://bugs.webkit.org/show_bug.cgi?id=174212
+
+        Reviewed by Yusuke Suzuki.
+
+        Implements the instance class fields proposal (https://tc39.es/proposal-class-fields/),
+        minus support for private fields (split into a separate patch).
+
+        In summary, class fields are initialized by a synthetic JSFunction. In its unlinked state,
+        the UnlinkedFunctionExecutable for the function includes an ordered list of JSTokenLocations
+        pointing to the start of each class field in the class. Each of these fields are parsed and
+        included as DefineFieldNodes, which implement the appropriate DefineField behaviour in the
+        proposal. This synthetic function is only created, and only loaded, if there are class fields
+        present. The decision to use a synthetic function was for simplicity. There are a number of
+        factors which make inlining the initialization complicated, though we may opt to do this in
+        the future. For reference, the complexities are: instance fields and constructor in different
+        currently in different parsing arenas, distinct scopes between the 2 which require work to manage,
+        and complexity in doing to this work for child classes, where the location of initialization can
+        depend, and in some cases occur more than once.
+
+        Computed property fields require a new bytecode, op_to_property_key, as an implementation
+        detail. It is necessary in the proposal to convert computed properties to property keys
+        during class evaluation, rather than during field initialization. Additionally, we allocate
+        the class lexical scope when computed class fields are used (previously, only when there was
+        a class name), as a location to keep the computed property keys. They can be loaded from the
+        scope via indexed keys.
+
+        To illustrate computed field names in action, consider the following pseudocode:
+
+            <during class evaluation>
+            1) fieldName = emitNode({expr})
+            2) fieldName = emitToPropertyKey(fieldName)
+            3) classScope[numComputedNames++] = fieldName
+
+            <during class field initialization>
+            1) fieldName = emitGetFromScope(classScope, computedFieldNameIndex++)
+            2) value = emitNode({initializer})
+            3) instance[fieldName] = value
+
+        The feature is currently hidden behind the feature flag JSC::Options::useClassFields.
+
 2020-01-15  Adrian Perez de Castro  <aperez@igalia.com>
 
         Offlineasm warnings with newer Ruby versions
index 2b79e7e..fd97e56 100644 (file)
@@ -32,7 +32,7 @@ namespace JSC {
 
 UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& source, const Identifier& ident, ConstructorKind kind, ConstructAbility ability)
 {
-    return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability);
+    return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability, NeedsClassFieldInitializer::No);
 }
     
 } // namespace JSC
index c4b1ee3..0a3b6ce 100644 (file)
@@ -59,7 +59,7 @@ SourceCode BuiltinExecutables::defaultConstructorSourceCode(ConstructorKind cons
     return SourceCode();
 }
 
-UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier& name)
+UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier& name, NeedsClassFieldInitializer needsClassFieldInitializer)
 {
     switch (constructorKind) {
     case ConstructorKind::None:
@@ -67,7 +67,7 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru
         break;
     case ConstructorKind::Base:
     case ConstructorKind::Extends:
-        return createExecutable(m_vm, defaultConstructorSourceCode(constructorKind), name, constructorKind, ConstructAbility::CanConstruct);
+        return createExecutable(m_vm, defaultConstructorSourceCode(constructorKind), name, constructorKind, ConstructAbility::CanConstruct, needsClassFieldInitializer);
     }
     ASSERT_NOT_REACHED();
     return nullptr;
@@ -75,10 +75,10 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru
 
 UnlinkedFunctionExecutable* BuiltinExecutables::createBuiltinExecutable(const SourceCode& code, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
 {
-    return createExecutable(m_vm, code, name, constructorKind, constructAbility);
+    return createExecutable(m_vm, code, name, constructorKind, constructAbility, NeedsClassFieldInitializer::No);
 }
 
-UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
+UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility, NeedsClassFieldInitializer needsClassFieldInitializer)
 {
     // FIXME: Can we just make MetaData computation be constexpr and have the compiler do this for us?
     // https://bugs.webkit.org/show_bug.cgi?id=193272
@@ -251,7 +251,7 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const S
         }
     }
 
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, needsClassFieldInitializer, isBuiltinDefaultClassConstructor);
     return functionExecutable;
 }
 
index 0f83d92..451638e 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "ExecutableInfo.h"
 #include "JSCBuiltins.h"
 #include "ParserModes.h"
 #include "SourceCode.h"
@@ -57,9 +58,9 @@ SourceCode name##Source();
 #undef EXPOSE_BUILTIN_EXECUTABLES
 
     static SourceCode defaultConstructorSourceCode(ConstructorKind);
-    UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name);
+    UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name, NeedsClassFieldInitializer);
 
-    static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility);
+    static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility, NeedsClassFieldInitializer);
 
     void finalizeUnconditionally();
 
index 154fff8..108307f 100644 (file)
@@ -167,6 +167,7 @@ namespace JSC {
     macro(meta) \
     macro(webAssemblyCompileStreamingInternal) \
     macro(webAssemblyInstantiateStreamingInternal) \
+    macro(instanceFieldInitializer) 
 
 namespace Symbols {
 #define DECLARE_BUILTIN_STATIC_SYMBOLS(name) extern JS_EXPORT_PRIVATE SymbolImpl::StaticSymbolImpl name##Symbol;
index a9bd483..862ccd8 100644 (file)
@@ -870,6 +870,12 @@ op :to_primitive,
         src: VirtualRegister,
     }
 
+op :to_property_key,
+    args: {
+        dst: VirtualRegister,
+        src: VirtualRegister,
+    }
+
 op :resolve_scope,
     args: {
         dst: VirtualRegister, # offset 1
index c018ddf..67f2cec 100644 (file)
@@ -163,6 +163,7 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru
     USES(OpResolveScopeForHoistingFuncDeclInEval, scope)
     USES(OpGetFromScope, scope)
     USES(OpToPrimitive, src)
+    USES(OpToPropertyKey, src)
     USES(OpTryGetById, base)
     USES(OpGetById, base)
     USES(OpGetByIdDirect, base)
@@ -381,6 +382,7 @@ void computeDefsForBytecodeIndexImpl(unsigned numVars, const Instruction* instru
     DEFS(OpResolveScopeForHoistingFuncDeclInEval, dst)
     DEFS(OpStrcat, dst)
     DEFS(OpToPrimitive, dst)
+    DEFS(OpToPropertyKey, dst)
     DEFS(OpCreateThis, dst)
     DEFS(OpCreatePromise, dst)
     DEFS(OpCreateGenerator, dst)
index 750900e..77f1929 100644 (file)
 namespace JSC {
     
 enum class DerivedContextType : uint8_t { None, DerivedConstructorContext, DerivedMethodContext };
-enum class EvalContextType    : uint8_t { None, FunctionEvalContext };
+enum class EvalContextType    : uint8_t { None, FunctionEvalContext, InstanceFieldEvalContext };
+enum class NeedsClassFieldInitializer : uint8_t { No, Yes };
 
 // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized.
 // https://bugs.webkit.org/show_bug.cgi?id=151547
 struct ExecutableInfo {
-    ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType)
+    ExecutableInfo(bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind, JSParserScriptMode scriptMode, SuperBinding superBinding, SourceParseMode parseMode, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isArrowFunctionContext, bool isClassContext, EvalContextType evalContextType)
         : m_usesEval(usesEval)
         , m_isStrictMode(isStrictMode)
         , m_isConstructor(isConstructor)
@@ -45,6 +46,7 @@ struct ExecutableInfo {
         , m_scriptMode(static_cast<unsigned>(scriptMode))
         , m_parseMode(parseMode)
         , m_derivedContextType(static_cast<unsigned>(derivedContextType))
+        , m_needsClassFieldInitializer(static_cast<unsigned>(needsClassFieldInitializer))
         , m_isArrowFunctionContext(isArrowFunctionContext)
         , m_isClassContext(isClassContext)
         , m_evalContextType(static_cast<unsigned>(evalContextType))
@@ -66,6 +68,7 @@ struct ExecutableInfo {
     EvalContextType evalContextType() const { return static_cast<EvalContextType>(m_evalContextType); }
     bool isArrowFunctionContext() const { return m_isArrowFunctionContext; }
     bool isClassContext() const { return m_isClassContext; }
+    NeedsClassFieldInitializer needsClassFieldInitializer() const { return static_cast<NeedsClassFieldInitializer>(m_needsClassFieldInitializer); }
 
 private:
     unsigned m_usesEval : 1;
@@ -77,6 +80,7 @@ private:
     unsigned m_scriptMode: 1;
     SourceParseMode m_parseMode;
     unsigned m_derivedContextType : 2;
+    unsigned m_needsClassFieldInitializer : 1;
     unsigned m_isArrowFunctionContext : 1;
     unsigned m_isClassContext : 1;
     unsigned m_evalContextType : 2;
index d5ddee8..20993e2 100644 (file)
@@ -79,6 +79,10 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM& vm, Structure* structure, CodeType code
     ASSERT(m_constructorKind == static_cast<unsigned>(info.constructorKind()));
     ASSERT(m_codeType == static_cast<unsigned>(codeType));
     ASSERT(m_didOptimize == static_cast<unsigned>(MixedTriState));
+    if (info.needsClassFieldInitializer() == NeedsClassFieldInitializer::Yes) {
+        createRareDataIfNecessary();
+        m_rareData->m_needsClassFieldInitializer = static_cast<unsigned>(NeedsClassFieldInitializer::Yes);
+    }
 }
 
 void UnlinkedCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor)
index ad1f90b..3d9fb3c 100644 (file)
@@ -331,6 +331,13 @@ public:
     unsigned age() const { return m_age; }
     void resetAge() { m_age = 0; }
 
+    NeedsClassFieldInitializer needsClassFieldInitializer() const
+    {
+        if (m_rareData)
+            return static_cast<NeedsClassFieldInitializer>(m_rareData->m_needsClassFieldInitializer);
+        return NeedsClassFieldInitializer::No;
+    }
+
     void dump(PrintStream&) const;
 
     BytecodeLivenessAnalysis& livenessAnalysis(CodeBlock* codeBlock)
@@ -474,6 +481,8 @@ public:
         Vector<InstructionStream::Offset> m_opProfileControlFlowBytecodeOffsets;
         Vector<BitVector> m_bitVectors;
         Vector<ConstantIdentifierSetEntry> m_constantIdentifierSets;
+
+        unsigned m_needsClassFieldInitializer : 1;
     };
 
     void addOutOfLineJumpTarget(InstructionStream::Offset, int target);
index dfc8a40..4dc0e91 100644 (file)
@@ -57,8 +57,9 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock(
     JSParserStrictMode strictMode = executable->isInStrictContext() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict;
     JSParserScriptMode scriptMode = executable->scriptMode();
     ASSERT(isFunctionParseMode(executable->parseMode()));
+    Vector<JSTextPosition>* instanceFieldLocations = executable->instanceFieldLocations();
     std::unique_ptr<FunctionNode> function = parse<FunctionNode>(
-        vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr);
+        vm, source, executable->name(), builtinMode, strictMode, scriptMode, executable->parseMode(), executable->superBinding(), error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None, nullptr, instanceFieldLocations);
 
     if (!function) {
         ASSERT(error.isValid());
@@ -70,7 +71,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock(
 
     bool isClassContext = executable->superBinding() == SuperBinding::Needed;
 
-    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), false, isClassContext, EvalContextType::FunctionEvalContext), codeGenerationMode);
+    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(vm, FunctionCode, ExecutableInfo(function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind(), scriptMode, executable->superBinding(), parseMode, executable->derivedContextType(), executable->needsClassFieldInitializer(), false, isClassContext, EvalContextType::FunctionEvalContext), codeGenerationMode);
 
     VariableEnvironment parentScopeTDZVariables = executable->parentScopeTDZVariables();
     error = BytecodeGenerator::generate(vm, function.get(), source, result, codeGenerationMode, &parentScopeTDZVariables);
@@ -81,7 +82,7 @@ static UnlinkedFunctionCodeBlock* generateUnlinkedFunctionCodeBlock(
     return result;
 }
 
-UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor)
+UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor)
     : Base(vm, structure)
     , m_firstLineOffset(node->firstLine() - parentSource.firstLine().oneBasedInt())
     , m_isInStrictContext(node->isInStrictContext())
@@ -108,6 +109,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* struct
     , m_functionMode(static_cast<unsigned>(node->functionMode()))
     , m_derivedContextType(static_cast<unsigned>(derivedContextType))
     , m_isGeneratedFromCache(false)
+    , m_needsClassFieldInitializer(static_cast<unsigned>(needsClassFieldInitializer))
     , m_unlinkedCodeBlockForCall()
     , m_unlinkedCodeBlockForConstruct()
     , m_name(node->ident())
@@ -121,6 +123,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* struct
     ASSERT(m_superBinding == static_cast<unsigned>(node->superBinding()));
     ASSERT(m_derivedContextType == static_cast<unsigned>(derivedContextType));
     ASSERT(!(m_isBuiltinDefaultClassConstructor && constructorKind() == ConstructorKind::None));
+    ASSERT(!m_needsClassFieldInitializer || (isClassConstructorFunction() || derivedContextType == DerivedContextType::DerivedConstructorContext));
     if (!node->classSource().isNull())
         setClassSource(node->classSource());
     if (parentScopeTDZVariables)
index 03b8572..9873832 100644 (file)
@@ -34,6 +34,7 @@
 #include "Intrinsic.h"
 #include "JSCast.h"
 #include "ParserModes.h"
+#include "ParserTokens.h"
 #include "RegExp.h"
 #include "SourceCode.h"
 #include "VariableEnvironment.h"
@@ -69,10 +70,10 @@ public:
         return &vm.unlinkedFunctionExecutableSpace.space;
     }
 
-    static UnlinkedFunctionExecutable* create(VM& vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor = false)
+    static UnlinkedFunctionExecutable* create(VM& vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional<CompactVariableMap::Handle> parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor = false)
     {
         UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm.heap))
-            UnlinkedFunctionExecutable(vm, vm.unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, WTFMove(parentScopeTDZVariables), derivedContextType, isBuiltinDefaultClassConstructor);
+            UnlinkedFunctionExecutable(vm, vm.unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, WTFMove(parentScopeTDZVariables), derivedContextType, needsClassFieldInitializer, isBuiltinDefaultClassConstructor);
         instance->finishCreation(vm);
         return instance;
     }
@@ -208,10 +209,27 @@ public:
         String m_sourceURLDirective;
         String m_sourceMappingURLDirective;
         CompactVariableMap::Handle m_parentScopeTDZVariables;
+        Vector<JSTextPosition> m_instanceFieldLocations;
     };
 
+    NeedsClassFieldInitializer needsClassFieldInitializer() const { return static_cast<NeedsClassFieldInitializer>(m_needsClassFieldInitializer); }
+
+    Vector<JSTextPosition>* instanceFieldLocations() const
+    {
+        if (m_rareData)
+            return &m_rareData->m_instanceFieldLocations;
+        return nullptr;
+    }
+
+    void setInstanceFieldLocations(Vector<JSTextPosition>&& instanceFieldLocations)
+    {
+        if (instanceFieldLocations.isEmpty())
+            return;
+        ensureRareData().m_instanceFieldLocations = WTFMove(instanceFieldLocations);
+    }
+
 private:
-    UnlinkedFunctionExecutable(VM&, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional<CompactVariableMap::Handle>,  JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable(VM&, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional<CompactVariableMap::Handle>,  JSC::DerivedContextType, JSC::NeedsClassFieldInitializer, bool isBuiltinDefaultClassConstructor);
     UnlinkedFunctionExecutable(Decoder&, const CachedFunctionExecutable&);
 
     void decodeCachedCodeBlocks(VM&);
@@ -248,6 +266,7 @@ private:
     unsigned m_functionMode : 2; // FunctionMode
     unsigned m_derivedContextType: 2;
     unsigned m_isGeneratedFromCache : 1;
+    unsigned m_needsClassFieldInitializer : 1;
 
     union {
         WriteBarrier<UnlinkedFunctionCodeBlock> m_unlinkedCodeBlockForCall;
index b52fc62..9101d10 100644 (file)
@@ -376,7 +376,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
     bool shouldCaptureSomeOfTheThings = shouldEmitDebugHooks() || functionNode->needsActivation() || containsArrowOrEvalButNotInArrowBlock;
 
     bool shouldCaptureAllOfTheThings = shouldEmitDebugHooks() || codeBlock->usesEval();
-    bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction()));
+    bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())) && parseMode != SourceParseMode::InstanceFieldInitializerMode;
 
     if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) {
         // Generator and AsyncFunction never provides "arguments". "arguments" reference will be resolved in an upper generator function scope.
@@ -600,7 +600,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         }
 
         bool shouldCreateArgumensVariable = !haveParameterNamedArguments
-            && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(m_codeBlock->parseMode());
+            && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::InstanceFieldInitializerMode).contains(m_codeBlock->parseMode());
         shouldCreateArgumentsVariableInParameterScope = shouldCreateArgumensVariable && !isSimpleParameterList;
         // Do not create arguments variable in case of Arrow function. Value will be loaded from parent scope
         if (shouldCreateArgumensVariable && !shouldCreateArgumentsVariableInParameterScope) {
@@ -695,6 +695,8 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
                 case ConstructorKind::None:
                 case ConstructorKind::Base:
                     emitCreateThis(&m_thisRegister);
+                    if (Options::useClassFields())
+                        emitInstanceFieldInitializationIfNeeded(&m_thisRegister, &m_calleeRegister, m_scopeNode->position(), m_scopeNode->position(), m_scopeNode->position());
                     break;
                 case ConstructorKind::Extends:
                     moveEmptyValue(&m_thisRegister);
@@ -2732,6 +2734,19 @@ RegisterID* BytecodeGenerator::emitCreateArgumentsButterfly(RegisterID* dst)
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
+{
+    if (!(isConstructor() || isDerivedConstructorContext()) || needsClassFieldInitializer() == NeedsClassFieldInitializer::No)
+        return dst;
+
+    RefPtr<RegisterID> initializer = emitDirectGetById(newTemporary(), constructor, propertyNames().builtinNames().instanceFieldInitializerPrivateName());
+    CallArguments args(*this, nullptr);
+    emitMove(args.thisRegister(), dst);
+    emitCall(newTemporary(), initializer.get(), NoExpectedFunction, args, divot, divotStart, divotEnd, DebuggableCall::No);
+
+    return dst;
+}
+
 void BytecodeGenerator::emitTDZCheck(RegisterID* target)
 {
     OpCheckTdz::emit(this, target);
@@ -3013,9 +3028,9 @@ RegisterID* BytecodeGenerator::emitNewMethodDefinition(RegisterID* dst, MethodDe
 }
 
 RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name,
-    const Identifier& ecmaName, const SourceCode& classSource)
+    const Identifier& ecmaName, const SourceCode& classSource, NeedsClassFieldInitializer needsClassFieldInitializer)
 {
-    UnlinkedFunctionExecutable* executable = m_vm.builtinExecutables()->createDefaultConstructor(constructorKind, name);
+    UnlinkedFunctionExecutable* executable = m_vm.builtinExecutables()->createDefaultConstructor(constructorKind, name, needsClassFieldInitializer);
     executable->setInvalidTypeProfilingOffsets();
     executable->setEcmaName(ecmaName);
     executable->setClassSource(classSource);
@@ -3026,6 +3041,33 @@ RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, Constr
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived)
+{
+    DerivedContextType newDerivedContextType;
+    SuperBinding superBinding;
+    if (!isDerived) {
+        newDerivedContextType = DerivedContextType::None;
+        superBinding = SuperBinding::NotNeeded;
+    } else {
+        newDerivedContextType = DerivedContextType::DerivedMethodContext;
+        superBinding = SuperBinding::Needed;
+    }
+
+    Optional<CompactVariableMap::Handle> variablesUnderTDZ = getVariablesUnderTDZ();
+    SourceParseMode parseMode = SourceParseMode::InstanceFieldInitializerMode;
+    ConstructAbility constructAbility = ConstructAbility::CannotConstruct;
+
+    const bool alwaysStrictInClass = true;
+    FunctionMetadataNode metadata(parserArena(), JSTokenLocation(), JSTokenLocation(), 0, 0, 0, 0, 0, alwaysStrictInClass, ConstructorKind::None, superBinding, 0, parseMode, false);
+    metadata.finishParsing(m_scopeNode->source(), Identifier(), FunctionMode::MethodDefinition);
+    auto initializer = UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), &metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(variablesUnderTDZ), newDerivedContextType, NeedsClassFieldInitializer::No);
+    initializer->setInstanceFieldLocations(WTFMove(instanceFieldLocations));
+
+    unsigned index = m_codeBlock->addFunctionExpr(initializer);
+    OpNewFuncExp::emit(this, dst, scopeRegister(), index);
+    return dst;
+}
+
 RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadataNode* function)
 {
     unsigned index = m_codeBlock->addFunctionDecl(makeFunction(function));
@@ -3040,7 +3082,8 @@ RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadata
     return dst;
 }
 
-void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name)
+template<typename LazyNameRegisterFn>
+void BytecodeGenerator::emitSetFunctionNameIfNeededImpl(ExpressionNode* valueNode, RegisterID* value, const LazyNameRegisterFn& lazyNameFn)
 {
     if (valueNode->isBaseFuncExprNode()) {
         FunctionMetadataNode* metadata = static_cast<BaseFuncExprNode*>(valueNode)->metadata();
@@ -3055,11 +3098,23 @@ void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, R
     } else
         return;
 
+    RegisterID* name = lazyNameFn();
+
     // FIXME: We should use an op_call to an internal function here instead.
     // https://bugs.webkit.org/show_bug.cgi?id=155547
     OpSetFunctionName::emit(this, value, name);
 }
 
+void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, const Identifier& ident)
+{
+    emitSetFunctionNameIfNeededImpl(valueNode, value, [=]() { return emitLoad(newTemporary(), ident); });
+}
+
+void BytecodeGenerator::emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name)
+{
+    emitSetFunctionNameIfNeededImpl(valueNode, value, [=]() { return name; });
+}
+
 RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall debuggableCall)
 {
     return emitCall<OpCall>(dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd, debuggableCall);
@@ -3403,6 +3458,12 @@ void BytecodeGenerator::emitToPrimitive(RegisterID* dst, RegisterID* src)
     OpToPrimitive::emit(this, dst, src);
 }
 
+RegisterID* BytecodeGenerator::emitToPropertyKey(RegisterID* dst, RegisterID* src)
+{
+    OpToPropertyKey::emit(this, dst, src);
+    return dst;
+}
+
 void BytecodeGenerator::emitGetScope()
 {
     OpGetScope::emit(this, scopeRegister());
@@ -4233,7 +4294,7 @@ void BytecodeGenerator::popIndexedForInScope(RegisterID* localRegister)
 
 RegisterID* BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment(const Identifier& identifier)
 {
-    ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode);
+    ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode || m_codeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode);
 
     return emitResolveScope(nullptr, variable(identifier, ThisResolutionType::Scoped));
 }
@@ -4257,6 +4318,14 @@ RegisterID* BytecodeGenerator::emitLoadDerivedConstructorFromArrowFunctionLexica
     return emitGetFromScope(newTemporary(), emitLoadArrowFunctionLexicalEnvironment(propertyNames().builtinNames().derivedConstructorPrivateName()), protoScopeVar, ThrowIfNotFound);
 }
 
+RegisterID* BytecodeGenerator::emitLoadDerivedConstructor()
+{
+    ASSERT(constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext());
+    if (constructorKind() == ConstructorKind::Extends)
+        return &m_calleeRegister;
+    return emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
+}
+
 RegisterID* BytecodeGenerator::ensureThis()
 {
     if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) {
index a82afdf..c9e4f69 100644 (file)
@@ -402,6 +402,7 @@ namespace JSC {
         ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); }
         SuperBinding superBinding() const { return m_codeBlock->superBinding(); }
         JSParserScriptMode scriptMode() const { return m_codeBlock->scriptMode(); }
+        NeedsClassFieldInitializer needsClassFieldInitializer() const { return m_codeBlock->needsClassFieldInitializer(); }
 
         template<typename Node, typename UnlinkedCodeBlock>
         static ParserError generate(VM& vm, Node* node, const SourceCode& sourceCode, UnlinkedCodeBlock* unlinkedCodeBlock, OptionSet<CodeGenerationMode> codeGenerationMode, const VariableEnvironment* environment)
@@ -554,14 +555,14 @@ namespace JSC {
             return emitNodeInTailPosition(nullptr, n);
         }
 
-        RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype)
+        RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype, Vector<JSTextPosition>& instanceFieldLocations)
         {
             ASSERT(constructor->refCount() && prototype->refCount());
             if (UNLIKELY(!m_vm.isSafeToRecurse()))
                 return emitThrowExpressionTooDeepException();
             if (UNLIKELY(n->needsDebugHook()))
                 emitDebugHook(n);
-            return n->emitBytecode(*this, constructor, prototype);
+            return n->emitBytecode(*this, constructor, prototype, &instanceFieldLocations);
         }
 
         RegisterID* emitNodeForProperty(RegisterID* dst, ExpressionNode* node)
@@ -727,6 +728,7 @@ namespace JSC {
         RegisterID* emitCreateGenerator(RegisterID* dst, RegisterID* newTarget);
         RegisterID* emitCreateAsyncGenerator(RegisterID* dst, RegisterID* newTarget);
         RegisterID* emitCreateArgumentsButterfly(RegisterID* dst);
+        RegisterID* emitInstanceFieldInitializationIfNeeded(RegisterID* dst, RegisterID* constructor, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
         void emitTDZCheck(RegisterID* target);
         bool needsTDZCheck(const Variable&);
         void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope);
@@ -742,12 +744,14 @@ namespace JSC {
 
         RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*);
         RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*);
-        RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource);
+        RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource, NeedsClassFieldInitializer);
+        RegisterID* emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived);
         RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*);
         RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*);
         RegisterID* emitNewRegExp(RegisterID* dst, RegExp*);
 
         void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name);
+        void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, const Identifier&);
 
         RegisterID* moveLinkTimeConstant(RegisterID* dst, LinkTimeConstant);
         RegisterID* moveEmptyValue(RegisterID* dst);
@@ -831,6 +835,7 @@ namespace JSC {
         RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, RegisterID* lazyThis, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
         RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count);
         void emitToPrimitive(RegisterID* dst, RegisterID* src);
+        RegisterID* emitToPropertyKey(RegisterID* dst, RegisterID* src);
 
         ResolveType resolveType();
         RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&);
@@ -951,6 +956,7 @@ namespace JSC {
         void emitPutNewTargetToArrowFunctionContextScope();
         void emitPutDerivedConstructorToArrowFunctionContextScope();
         RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
+        RegisterID* emitLoadDerivedConstructor();
 
         void emitDebugHook(DebugHookType, const JSTextPosition&);
         void emitDebugHook(DebugHookType, unsigned line, unsigned charOffset, unsigned lineStart);
@@ -970,6 +976,9 @@ namespace JSC {
         bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister);
         void emitFinallyCompletion(FinallyContext&, Label& normalCompletionLabel);
 
+        template<typename LazyNameRegisterFn>
+        void emitSetFunctionNameIfNeededImpl(ExpressionNode*, RegisterID*, const LazyNameRegisterFn&);
+
     public:
         void pushFinallyControlFlowScope(FinallyContext&);
         void popFinallyControlFlowScope();
@@ -1117,9 +1126,12 @@ namespace JSC {
         {
             DerivedContextType newDerivedContextType = DerivedContextType::None;
 
+            NeedsClassFieldInitializer needsClassFieldInitializer = metadata->isConstructorAndNeedsClassFieldInitializer() ? NeedsClassFieldInitializer::Yes : NeedsClassFieldInitializer::No;
             if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncArrowFunctionBodyMode).contains(metadata->parseMode())) {
-                if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
+                if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) {
                     newDerivedContextType = DerivedContextType::DerivedConstructorContext;
+                    needsClassFieldInitializer = m_codeBlock->needsClassFieldInitializer();
+                }
                 else if (m_codeBlock->isClassContext() || isDerivedClassContext())
                     newDerivedContextType = DerivedContextType::DerivedMethodContext;
             }
@@ -1133,7 +1145,7 @@ namespace JSC {
             if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() != ConstructorKind::None)
                 constructAbility = ConstructAbility::CanConstruct;
 
-            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType);
+            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType, needsClassFieldInitializer);
         }
 
         Optional<CompactVariableMap::Handle> getVariablesUnderTDZ();
index 3326fb3..b61200a 100644 (file)
@@ -178,7 +178,7 @@ RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst
 
 static RegisterID* emitHomeObjectForCallee(BytecodeGenerator& generator)
 {
-    if (generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) {
+    if ((generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) && generator.parseMode() != SourceParseMode::InstanceFieldInitializerMode) {
         RegisterID* derivedConstructor = generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
         return generator.emitGetById(generator.newTemporary(), derivedConstructor, generator.propertyNames().builtinNames().homeObjectPrivateName());
     }
@@ -542,13 +542,23 @@ static inline void emitPutHomeObject(BytecodeGenerator& generator, RegisterID* f
     generator.emitPutById(function, generator.propertyNames().builtinNames().homeObjectPrivateName(), homeObject);
 }
 
-RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype)
+RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations)
 {
     // Fast case: this loop just handles regular value properties.
     PropertyListNode* p = this;
     RegisterID* dst = nullptr;
     for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) {
         dst = p->m_node->isInstanceClassProperty() ? prototype : dstOrConstructor;
+
+        if (p->isComputedClassField())
+            emitSaveComputedFieldName(generator, *p->m_node);
+
+        if (p->isInstanceClassField()) {
+            ASSERT(instanceFieldLocations);
+            instanceFieldLocations->append(p->position());
+            continue;
+        }
+
         emitPutConstantProperty(generator, dst, *p->m_node);
     }
 
@@ -596,6 +606,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             PropertyNode* node = p->m_node;
             dst = node->isInstanceClassProperty() ? prototype : dstOrConstructor;
 
+            if (p->isComputedClassField())
+                emitSaveComputedFieldName(generator, *p->m_node);
+
+            if (p->isInstanceClassField()) {
+                ASSERT(instanceFieldLocations);
+                ASSERT(node->m_type & PropertyNode::Constant);
+                instanceFieldLocations->append(p->position());
+                continue;
+            }
+
             // Handle regular values.
             if (node->m_type & PropertyNode::Constant) {
                 emitPutConstantProperty(generator, dst, *node);
@@ -717,6 +737,23 @@ void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, Reg
     generator.emitDirectPutByVal(newObj, propertyName.get(), value.get());
 }
 
+void PropertyListNode::emitSaveComputedFieldName(BytecodeGenerator& generator, PropertyNode& node)
+{
+    ASSERT(node.isComputedClassField());
+    RefPtr<RegisterID> propertyExpr;
+
+    // The 'name' refers to a synthetic numeric variable name in the private name scope, where the property key is saved for later use.
+    const Identifier& description = *node.name();
+    Variable var = generator.variable(description);
+    ASSERT(!var.local());
+
+    propertyExpr = generator.emitNode(node.m_expression);
+    RegisterID* propertyName = generator.emitToPropertyKey(generator.newTemporary(), propertyExpr.get());
+
+    RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+    generator.emitPutToScope(scope.get(), var, propertyName, ThrowIfNotFound, InitializationMode::ConstInitialization);
+}
+
 // ------------------------------ BracketAccessorNode --------------------------------
 
 static bool isNonIndexStringElement(ExpressionNode& element)
@@ -923,7 +960,14 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
         
         if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor)
             generator.emitPutThisToArrowFunctionContextScope();
-        
+
+        // Initialize instance fields after super-call.
+        if (Options::useClassFields() && generator.needsClassFieldInitializer() == NeedsClassFieldInitializer::Yes) {
+            ASSERT(generator.isConstructor() || generator.isDerivedConstructorContext());
+            func = generator.emitLoadDerivedConstructor();
+            generator.emitInstanceFieldInitializationIfNeeded(generator.thisRegister(), func.get(), divot(), divotStart(), divotEnd());
+        }
+
         return ret;
     }
 
@@ -4266,6 +4310,48 @@ RegisterID* AwaitExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     return generator.move(generator.finalDestination(dst), value.get());
 }
 
+// ------------------------------ DefineFieldNode ---------------------------------
+
+void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
+{
+    RefPtr<RegisterID> value = generator.newTemporary();
+
+    if (!m_assign)
+        generator.emitLoad(value.get(), jsUndefined());
+    else {
+        generator.emitNode(value.get(), m_assign);
+        if (m_ident)
+            generator.emitSetFunctionNameIfNeeded(m_assign, value.get(), *m_ident);
+    }
+
+    switch (m_type) {
+    case DefineFieldNode::Type::Name: {
+        // FIXME: Improve performance of public class fields
+        // https://bugs.webkit.org/show_bug.cgi?id=198330
+        RefPtr<RegisterID> propertyName = generator.emitLoad(nullptr, *m_ident);
+        generator.emitCallDefineProperty(generator.thisRegister(), propertyName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position);
+        break;
+    }
+    case DefineFieldNode::Type::ComputedName: {
+        // FIXME: Improve performance of public class fields
+        // https://bugs.webkit.org/show_bug.cgi?id=198330
+
+        // For ComputedNames, the expression has already been evaluated earlier during evaluation of a ClassExprNode.
+        // Here, `m_ident` refers to an integer ID in a class lexical scope, containing the value already converted to an Expression.
+        Variable var = generator.variable(*m_ident);
+        ASSERT_WITH_MESSAGE(!var.local(), "Computed names must be stored in captured variables");
+
+        generator.emitExpressionInfo(position(), position(), position() + 1);
+        RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var);
+        RefPtr<RegisterID> privateName = generator.newTemporary();
+        generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound);
+        generator.emitProfileType(privateName.get(), var, m_position, JSTextPosition(-1, m_position.offset + m_ident->length(), -1));
+        generator.emitCallDefineProperty(generator.thisRegister(), privateName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position);
+        break;
+    }
+    }
+}
+
 // ------------------------------ ClassDeclNode ---------------------------------
 
 void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
@@ -4277,7 +4363,7 @@ void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 
 RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    if (!m_name.isNull())
+    if (m_needsLexicalScope)
         generator.pushLexicalScope(this, BytecodeGenerator::TDZCheckOptimization::Optimize, BytecodeGenerator::NestedScopeType::IsNested);
 
     RefPtr<RegisterID> superclass;
@@ -4289,15 +4375,18 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     RefPtr<RegisterID> constructor = generator.tempDestination(dst);
     bool needsHomeObject = false;
 
+    auto needsClassFieldInitializer = this->hasInstanceFields() ? NeedsClassFieldInitializer::Yes : NeedsClassFieldInitializer::No;
+
     if (m_constructorExpression) {
         ASSERT(m_constructorExpression->isFuncExprNode());
         FunctionMetadataNode* metadata = static_cast<FuncExprNode*>(m_constructorExpression)->metadata();
         metadata->setEcmaName(ecmaName());
         metadata->setClassSource(m_classSource);
+        metadata->setNeedsClassFieldInitializer(needsClassFieldInitializer == NeedsClassFieldInitializer::Yes);
         constructor = generator.emitNode(constructor.get(), m_constructorExpression);
         needsHomeObject = m_classHeritage || metadata->superBinding() == SuperBinding::Needed;
     } else
-        constructor = generator.emitNewDefaultConstructor(constructor.get(), m_classHeritage ? ConstructorKind::Extends : ConstructorKind::Base, m_name, ecmaName(), m_classSource);
+        constructor = generator.emitNewDefaultConstructor(constructor.get(), m_classHeritage ? ConstructorKind::Extends : ConstructorKind::Base, m_name, ecmaName(), m_classSource, needsClassFieldInitializer);
 
     const auto& propertyNames = generator.propertyNames();
     RefPtr<RegisterID> prototype = generator.emitNewObject(generator.newTemporary());
@@ -4343,14 +4432,27 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     RefPtr<RegisterID> prototypeNameRegister = generator.emitLoad(nullptr, propertyNames.prototype);
     generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position);
 
-    if (m_classElements)
-        generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get());
+    if (m_classElements) {
+        Vector<JSTextPosition> instanceFieldLocations;
+        generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get(), instanceFieldLocations);
+        if (!instanceFieldLocations.isEmpty()) {
+            RefPtr<RegisterID> instanceFieldInitializer = generator.emitNewInstanceFieldInitializerFunction(generator.newTemporary(), WTFMove(instanceFieldLocations), m_classHeritage);
+
+            // FIXME: Skip this if the initializer function isn't going to need a home object (no eval or super properties)
+            // https://bugs.webkit.org/show_bug.cgi?id=196867
+            emitPutHomeObject(generator, instanceFieldInitializer.get(), prototype.get());
 
-    if (!m_name.isNull()) {
-        Variable classNameVar = generator.variable(m_name);
-        RELEASE_ASSERT(classNameVar.isResolved());
-        RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar);
-        generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization);
+            generator.emitDirectPutById(constructor.get(), generator.propertyNames().builtinNames().instanceFieldInitializerPrivateName(), instanceFieldInitializer.get(), PropertyNode::Unknown);
+        }
+    }
+
+    if (m_needsLexicalScope) {
+        if (!m_name.isNull()) {
+            Variable classNameVar = generator.variable(m_name);
+            RELEASE_ASSERT(classNameVar.isResolved());
+            RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar);
+            generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization);
+        }
         generator.popLexicalScope(this);
     }
 
index 3258faa..c3d4c3f 100644 (file)
@@ -253,7 +253,7 @@ JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSOb
     VariableEnvironment variablesUnderTDZ;
     JSScope::collectClosureVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
 
-    auto* eval = DirectEvalExecutable::create(globalObject, makeSource(script, callFrame->callerSourceOrigin(vm)), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
+    auto* eval = DirectEvalExecutable::create(globalObject, makeSource(script, callFrame->callerSourceOrigin(vm)), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->needsClassFieldInitializer(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
     if (UNLIKELY(catchScope.exception())) {
         exception = catchScope.exception();
         catchScope.clearException();
index 2477af0..b182317 100644 (file)
@@ -293,6 +293,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I
 
     case op_yield:
     case op_create_generator_frame_environment:
+    case op_to_property_key: // FIXME: op_to_property_key will need DFG/FTL support to ship class fields.
     case llint_program_prologue:
     case llint_eval_prologue:
     case llint_module_program_prologue:
index 9d3d45e..570b0af 100644 (file)
@@ -136,7 +136,9 @@ JSValue eval(JSGlobalObject* globalObject, CallFrame* callFrame)
     }
 
     EvalContextType evalContextType;
-    if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode()))
+    if (callerUnlinkedCodeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode)
+        evalContextType = EvalContextType::InstanceFieldEvalContext;
+    else if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode()))
         evalContextType = EvalContextType::FunctionEvalContext;
     else if (callerUnlinkedCodeBlock->codeType() == EvalCode)
         evalContextType = callerUnlinkedCodeBlock->evalContextType();
@@ -162,7 +164,7 @@ JSValue eval(JSGlobalObject* globalObject, CallFrame* callFrame)
         
         VariableEnvironment variablesUnderTDZ;
         JSScope::collectClosureVariablesUnderTDZ(callerScopeChain, variablesUnderTDZ);
-        eval = DirectEvalExecutable::create(globalObject, makeSource(programSource, callerCodeBlock->source().provider()->sourceOrigin()), callerCodeBlock->isStrictMode(), derivedContextType, isArrowFunctionContext, evalContextType, &variablesUnderTDZ);
+        eval = DirectEvalExecutable::create(globalObject, makeSource(programSource, callerCodeBlock->source().provider()->sourceOrigin()), callerCodeBlock->isStrictMode(), derivedContextType, callerUnlinkedCodeBlock->needsClassFieldInitializer(), isArrowFunctionContext, evalContextType, &variablesUnderTDZ);
         EXCEPTION_ASSERT(!!scope.exception() == !eval);
         if (!eval)
             return jsUndefined();
index 724578d..d771794 100644 (file)
@@ -425,6 +425,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_put_getter_setter_by_id)
         DEFINE_OP(op_put_getter_by_val)
         DEFINE_OP(op_put_setter_by_val)
+        DEFINE_OP(op_to_property_key)
 
         DEFINE_OP(op_get_internal_field)
         DEFINE_OP(op_put_internal_field)
@@ -599,6 +600,7 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_SLOW_OP(has_structure_property)
         DEFINE_SLOWCASE_SLOW_OP(resolve_scope)
         DEFINE_SLOWCASE_SLOW_OP(check_tdz)
+        DEFINE_SLOWCASE_SLOW_OP(to_property_key)
 
         default:
             RELEASE_ASSERT_NOT_REACHED();
index 28032a2..5656675 100644 (file)
@@ -621,6 +621,7 @@ namespace JSC {
         void emit_op_put_internal_field(const Instruction*);
         void emit_op_log_shadow_chicken_prologue(const Instruction*);
         void emit_op_log_shadow_chicken_tail(const Instruction*);
+        void emit_op_to_property_key(const Instruction*);
 
         void emitSlow_op_add(const Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_call(const Instruction*, Vector<SlowCaseEntry>::iterator&);
index b300125..88268d9 100644 (file)
@@ -357,6 +357,23 @@ void JIT::emit_op_to_primitive(const Instruction* currentInstruction)
 
 }
 
+void JIT::emit_op_to_property_key(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpToPropertyKey>();
+    VirtualRegister dst = bytecode.m_dst;
+    VirtualRegister src = bytecode.m_src;
+
+    emitGetVirtualRegister(src, regT0);
+
+    addSlowCase(branchIfNotCell(regT0));
+    Jump done = branchIfSymbol(regT0);
+    addSlowCase(branchIfNotString(regT0));
+
+    done.link(this);
+    if (src != dst)
+        emitPutVirtualRegister(dst);
+}
+
 void JIT::emit_op_set_function_name(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpSetFunctionName>();
index dae8a78..c1bb657 100644 (file)
@@ -343,6 +343,23 @@ void JIT::emit_op_to_primitive(const Instruction* currentInstruction)
         emitStore(dst, regT1, regT0);
 }
 
+void JIT::emit_op_to_property_key(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpToPropertyKey>();
+    VirtualRegister dst = bytecode.m_dst;
+    VirtualRegister src = bytecode.m_src;
+
+    emitLoad(src, regT1, regT0);
+
+    addSlowCase(branchIfNotCell(regT1));
+    Jump done = branchIfSymbol(regT0);
+    addSlowCase(branchIfNotString(regT0));
+
+    done.link(this);
+    if (src != dst)
+        emitStore(dst, regT1, regT0);
+}
+
 void JIT::emit_op_set_function_name(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpSetFunctionName>();
index 05a68c9..c95719f 100644 (file)
@@ -1959,6 +1959,22 @@ llintOpWithReturn(op_to_primitive, OpToPrimitive, macro (size, get, dispatch, re
 end)
 
 
+llintOpWithReturn(op_to_property_key, OpToPropertyKey, macro (size, get, dispatch, return)
+    get(m_src, t2)
+    loadConstantOrVariable(size, t2, t1, t0)
+    bineq t1, CellTag, .opToPropertyKeySlow
+    bbeq JSCell::m_type[t0], SymbolType, .done
+    bbneq JSCell::m_type[t0], StringType, .opToPropertyKeySlow
+
+.done:
+    return(t1, t0)
+
+.opToPropertyKeySlow:
+    callSlowPath(_slow_path_to_property_key)
+    dispatch()
+end)
+
+
 commonOp(llint_op_catch, macro() end, macro (size)
     # This is where we end up from the JIT's throw trampoline (because the
     # machine code return address will be set to _llint_op_catch), and from
index 72cdafe..509b4a3 100644 (file)
@@ -2098,6 +2098,23 @@ llintOpWithReturn(op_to_primitive, OpToPrimitive, macro (size, get, dispatch, re
 end)
 
 
+llintOpWithReturn(op_to_property_key, OpToPropertyKey, macro (size, get, dispatch, return)
+    get(m_src, t2)
+    loadConstantOrVariable(size, t2, t0)
+
+    btqnz t0, notCellMask, .opToPropertyKeySlow
+    bbeq JSCell::m_type[t0], SymbolType, .done
+    bbneq JSCell::m_type[t0], StringType, .opToPropertyKeySlow
+
+.done:
+    return(t0)
+
+.opToPropertyKeySlow:
+    callSlowPath(_slow_path_to_property_key)
+    dispatch()
+end)
+
+
 commonOp(llint_op_catch, macro() end, macro (size)
     # This is where we end up from the JIT's throw trampoline (because the
     # machine code return address will be set to _llint_op_catch), and from
index cc597f5..dbd53ae 100644 (file)
@@ -405,6 +405,11 @@ public:
         return node;
     }
 
+    DefineFieldNode* createDefineField(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* initializer, DefineFieldNode::Type type)
+    {
+        return new (m_parserArena) DefineFieldNode(location, ident, initializer, type);
+    }
+
     ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor,
         ExpressionNode* parentClass, PropertyListNode* classElements)
     {
@@ -527,6 +532,7 @@ public:
         return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, propertyName), node, type, putType, superBinding, tag);
     }
     PropertyNode* createProperty(ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, ClassElementTag tag) { return new (m_parserArena) PropertyNode(propertyName, node, type, putType, superBinding, tag); }
+    PropertyNode* createProperty(const Identifier* identifier, ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, ClassElementTag tag) { return new (m_parserArena) PropertyNode(*identifier, propertyName, node, type, putType, superBinding, tag); }
     PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property) { return new (m_parserArena) PropertyListNode(location, property); }
     PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property, PropertyListNode* tail) { return new (m_parserArena) PropertyListNode(location, property, tail); }
 
index 9c7b9a2..003d089 100644 (file)
@@ -248,6 +248,7 @@ namespace JSC {
 
     inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag)
         : m_name(&name)
+        , m_expression(nullptr)
         , m_assign(assign)
         , m_type(type)
         , m_needsSuperBinding(superBinding == SuperBinding::Needed)
@@ -259,6 +260,7 @@ namespace JSC {
     
     inline PropertyNode::PropertyNode(ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag)
         : m_name(nullptr)
+        , m_expression(nullptr)
         , m_assign(assign)
         , m_type(type)
         , m_needsSuperBinding(superBinding == SuperBinding::Needed)
@@ -280,6 +282,18 @@ namespace JSC {
     {
     }
 
+    inline PropertyNode::PropertyNode(const Identifier& ident, ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, ClassElementTag tag)
+        : m_name(&ident)
+        , m_expression(name)
+        , m_assign(assign)
+        , m_type(type)
+        , m_needsSuperBinding(superBinding == SuperBinding::Needed)
+        , m_putType(putType)
+        , m_classElementTag(static_cast<unsigned>(tag))
+        , m_isOverriddenByDuplicate(false)
+    {
+    }
+
     inline PropertyListNode::PropertyListNode(const JSTokenLocation& location, PropertyNode* node)
         : ExpressionNode(location)
         , m_node(node)
@@ -998,6 +1012,14 @@ namespace JSC {
     {
     }
 
+    inline DefineFieldNode::DefineFieldNode(const JSTokenLocation& location, const Identifier* ident, ExpressionNode* assign, Type type)
+        : StatementNode(location)
+        , m_ident(ident)
+        , m_assign(assign)
+        , m_type(type)
+    {
+    }
+
     inline ClassDeclNode::ClassDeclNode(const JSTokenLocation& location, ExpressionNode* classDeclaration)
         : StatementNode(location)
         , m_classDeclaration(classDeclaration)
@@ -1013,6 +1035,7 @@ namespace JSC {
         , m_constructorExpression(constructorExpression)
         , m_classHeritage(classHeritage)
         , m_classElements(classElements)
+        , m_needsLexicalScope(!name.isNull() || PropertyListNode::shouldCreateLexicalScopeForClass(classElements))
     {
     }
 
index 30d7d6a..dd8edb6 100644 (file)
@@ -200,6 +200,7 @@ FunctionMetadataNode::FunctionMetadataNode(
         , m_isInStrictContext(isInStrictContext)
         , m_superBinding(static_cast<unsigned>(superBinding))
         , m_constructorKind(static_cast<unsigned>(constructorKind))
+        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
         , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
         , m_parseMode(mode)
         , m_startColumn(startColumn)
@@ -223,6 +224,7 @@ FunctionMetadataNode::FunctionMetadataNode(
         , m_isInStrictContext(isInStrictContext)
         , m_superBinding(static_cast<unsigned>(superBinding))
         , m_constructorKind(static_cast<unsigned>(constructorKind))
+        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
         , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
         , m_parseMode(mode)
         , m_startColumn(startColumn)
@@ -328,6 +330,31 @@ bool PropertyListNode::hasStaticallyNamedProperty(const Identifier& propName)
     return false;
 }
 
+// FIXME: calculate this feature once when parsing the property list.
+// https://bugs.webkit.org/show_bug.cgi?id=206174
+bool PropertyListNode::shouldCreateLexicalScopeForClass(PropertyListNode* list)
+{
+    while (list) {
+        if (list->m_node->isComputedClassField())
+            return true;
+        list = list->m_next;
+    }
+    return false;
+}
+
+// ------------------------------ ClassExprNode -----------------------------
+
+// FIXME: calculate this feature once when parsing the property list.
+// https://bugs.webkit.org/show_bug.cgi?id=206174
+bool PropertyListNode::hasInstanceFields() const
+{
+    for (auto list = this; list; list = list->m_next) {
+        if (list->m_node->isInstanceClassField())
+            return true;
+    }
+    return false;
+}
+
 VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables)
 {
     m_lexicalVariables.swap(lexicalVariables);
index 39be52a..2a83646 100644 (file)
@@ -251,6 +251,7 @@ namespace JSC {
         virtual bool isFuncDeclNode() const { return false; }
         virtual bool isModuleDeclarationNode() const { return false; }
         virtual bool isForOfNode() const { return false; }
+        virtual bool isDefineFieldNode() const { return false; }
 
     protected:
         int m_lastLine { -1 };
@@ -718,6 +719,7 @@ namespace JSC {
         PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag);
         PropertyNode(ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag);
         PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag);
+        PropertyNode(const Identifier&, ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, ClassElementTag);
 
         ExpressionNode* expressionName() const { return m_expression; }
         const Identifier* name() const { return m_name; }
@@ -727,7 +729,11 @@ namespace JSC {
         bool isClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) != ClassElementTag::No; }
         bool isStaticClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Static; }
         bool isInstanceClassProperty() const { return static_cast<ClassElementTag>(m_classElementTag) == ClassElementTag::Instance; }
+        bool isClassField() const { return isClassProperty() && !needsSuperBinding(); }
+        bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); }
         bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; }
+        bool hasComputedName() const { return m_expression; }
+        bool isComputedClassField() const { return isClassField() && hasComputedName(); }
         void setIsOverriddenByDuplicate() { m_isOverriddenByDuplicate = true; }
         PutType putType() const { return static_cast<PutType>(m_putType); }
 
@@ -741,7 +747,7 @@ namespace JSC {
         unsigned m_putType : 1;
         static_assert(1 << 2 > static_cast<unsigned>(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits");
         unsigned m_classElementTag : 2;
-        unsigned m_isOverriddenByDuplicate: 1;
+        unsigned m_isOverriddenByDuplicate : 1;
     };
 
     class PropertyListNode final : public ExpressionNode {
@@ -750,15 +756,27 @@ namespace JSC {
         PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*);
 
         bool hasStaticallyNamedProperty(const Identifier& propName);
+        bool isComputedClassField() const
+        {
+            return m_node->isComputedClassField();
+        }
+        bool isInstanceClassField() const
+        {
+            return m_node->isInstanceClassField();
+        }
+        bool hasInstanceFields() const;
 
-        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*);
+        static bool shouldCreateLexicalScopeForClass(PropertyListNode*);
+
+        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID*, RegisterID*, Vector<JSTextPosition>*);
 
     private:
         RegisterID* emitBytecode(BytecodeGenerator& generator, RegisterID* dst = nullptr) override
         {
-            return emitBytecode(generator, dst, nullptr);
+            return emitBytecode(generator, dst, nullptr, nullptr);
         }
         void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&);
+        void emitSaveComputedFieldName(BytecodeGenerator&, PropertyNode&);
 
         PropertyNode* m_node;
         PropertyListNode* m_next { nullptr };
@@ -2074,6 +2092,12 @@ namespace JSC {
         bool isInStrictContext() const { return m_isInStrictContext; }
         SuperBinding superBinding() { return static_cast<SuperBinding>(m_superBinding); }
         ConstructorKind constructorKind() { return static_cast<ConstructorKind>(m_constructorKind); }
+        bool isConstructorAndNeedsClassFieldInitializer() const { return m_needsClassFieldInitializer; }
+        void setNeedsClassFieldInitializer(bool value)
+        {
+            ASSERT(!value || constructorKind() != ConstructorKind::None);
+            m_needsClassFieldInitializer = value;
+        }
         bool isArrowFunctionBodyExpression() const { return m_isArrowFunctionBodyExpression; }
 
         void setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset)
@@ -2094,6 +2118,7 @@ namespace JSC {
         unsigned m_isInStrictContext : 1;
         unsigned m_superBinding : 1;
         unsigned m_constructorKind : 2;
+        unsigned m_needsClassFieldInitializer : 1;
         unsigned m_isArrowFunctionBodyExpression : 1;
         SourceParseMode m_parseMode;
         FunctionMode m_functionMode;
@@ -2210,6 +2235,21 @@ namespace JSC {
         ExpressionNode* m_argument;
     };
 
+    class DefineFieldNode final : public StatementNode {
+    public:
+        enum class Type { Name, ComputedName };
+        DefineFieldNode(const JSTokenLocation&, const Identifier*, ExpressionNode*, Type);
+
+    private:
+        void emitBytecode(BytecodeGenerator&, RegisterID* destination = nullptr) override;
+
+        bool isDefineFieldNode() const override { return true; }
+
+        const Identifier* m_ident;
+        ExpressionNode* m_assign;
+        Type m_type;
+    };
+
     class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode {
         JSC_MAKE_PARSER_ARENA_DELETABLE_ALLOCATED(ClassExprNode);
     public:
@@ -2222,6 +2262,7 @@ namespace JSC {
         void setEcmaName(const Identifier& name) { m_ecmaName = m_name.isNull() ? &name : &m_name; }
 
         bool hasStaticProperty(const Identifier& propName) { return m_classElements ? m_classElements->hasStaticallyNamedProperty(propName) : false; }
+        bool hasInstanceFields() const { return m_classElements ? m_classElements->hasInstanceFields() : false; }
 
     private:
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
@@ -2234,6 +2275,7 @@ namespace JSC {
         ExpressionNode* m_constructorExpression;
         ExpressionNode* m_classHeritage;
         PropertyListNode* m_classElements;
+        bool m_needsLexicalScope;
     };
 
     class DestructuringPatternNode : public ParserArenaFreeable {
index 83f3d72..6640bdd 100644 (file)
@@ -208,7 +208,7 @@ Parser<LexerType>::~Parser()
 }
 
 template <typename LexerType>
-String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition)
+String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition, const Vector<JSTextPosition>* instanceFieldLocations)
 {
     String parseError = String();
 
@@ -222,6 +222,8 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMo
         ParserFunctionInfo<ASTBuilder> functionInfo;
         if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode))
             m_parameters = createGeneratorParameters(context, functionInfo.parameterCount);
+        else if (parseMode == SourceParseMode::InstanceFieldInitializerMode)
+            m_parameters = context.createFormalParameterList();
         else
             m_parameters = parseFunctionParameters(context, parseMode, functionInfo);
 
@@ -254,7 +256,10 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMo
             sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode);
         else if (parsingContext == ParsingContext::FunctionConstructor)
             sourceElements = parseSingleFunction(context, functionConstructorParametersEndPosition);
-        else
+        else if (parseMode == SourceParseMode::InstanceFieldInitializerMode) {
+            ASSERT(instanceFieldLocations && !instanceFieldLocations->isEmpty());
+            sourceElements = parseInstanceFieldInitializerSourceElements(context, *instanceFieldLocations);
+        } else
             sourceElements = parseSourceElements(context, CheckForStrictMode);
     }
 
@@ -2113,6 +2118,7 @@ template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBo
     TreeBuilder& context, SyntaxChecker& syntaxChecker, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart,
     ConstructorKind constructorKind, SuperBinding superBinding, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode)
 {
+    SetForScope<bool> overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, bodyType == StandardFunctionBodyBlock ? false : m_parserState.isParsingClassFieldInitializer);
     bool isArrowFunctionBodyExpression = bodyType == ArrowFunctionBodyExpression;
     if (!isArrowFunctionBodyExpression) {
         next();
@@ -2165,6 +2171,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode)
     case SourceParseMode::ProgramMode:
     case SourceParseMode::ModuleAnalyzeMode:
     case SourceParseMode::ModuleEvaluateMode:
+    case SourceParseMode::InstanceFieldInitializerMode:
         RELEASE_ASSERT_NOT_REACHED();
         return "";
     }
@@ -2206,6 +2213,7 @@ static const char* stringForFunctionMode(SourceParseMode mode)
     case SourceParseMode::ProgramMode:
     case SourceParseMode::ModuleAnalyzeMode:
     case SourceParseMode::ModuleEvaluateMode:
+    case SourceParseMode::InstanceFieldInitializerMode:
         RELEASE_ASSERT_NOT_REACHED();
         return "";
     }
@@ -2853,6 +2861,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
         parentClass = parseMemberExpression(context);
         failIfFalse(parentClass, "Cannot parse the parent class name");
     }
+    classScope->setIsClassScope();
     const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : ConstructorKind::Base;
 
     consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body");
@@ -2860,6 +2869,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
     TreeExpression constructor = 0;
     TreePropertyList classElements = 0;
     TreePropertyList classElementsTail = 0;
+    unsigned numComputedFields = 0;
     while (!match(CLOSEBRACE)) {
         if (match(SEMICOLON)) {
             next();
@@ -2871,6 +2881,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
 
         // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode.
         ClassElementTag tag = ClassElementTag::Instance;
+        auto type = PropertyNode::Constant;
         if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm.propertyNames->staticKeyword) {
             SavePoint savePoint = createSavePoint();
             next();
@@ -2904,7 +2915,8 @@ parseMethod:
                 if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
                     ident = m_token.m_data.ident;
                     next();
-                    if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken())
+                    // We match SEMICOLON as a special case for a field called 'async' without initializer.
+                    if (match(OPENPAREN) || match(COLON) || match(SEMICOLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken())
                         break;
                     if (UNLIKELY(consume(TIMES)))
                         parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
@@ -2932,6 +2944,7 @@ parseMethod:
         case OPENBRACKET:
             next();
             computedPropertyName = parseAssignmentExpression(context);
+            type = static_cast<PropertyNode::Type>(type | PropertyNode::Computed);
             failIfFalse(computedPropertyName, "Cannot parse computed property name");
             handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name");
             break;
@@ -2944,9 +2957,38 @@ parseMethod:
         TreeProperty property;
         const bool alwaysStrictInsideClass = true;
         if (isGetter || isSetter) {
-            property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter,
-                methodStart, ConstructorKind::None, tag);
+            type = static_cast<PropertyNode::Type>(type & ~PropertyNode::Constant);
+            type = static_cast<PropertyNode::Type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter));
+            property = parseGetterSetter(context, alwaysStrictInsideClass, type, methodStart, ConstructorKind::None, tag);
             failIfFalse(property, "Cannot parse this method");
+        } else if (Options::useClassFields() && !match(OPENPAREN) && tag == ClassElementTag::Instance && parseMode == SourceParseMode::MethodMode) {
+            ASSERT(!isGetter && !isSetter);
+            if (ident)
+                semanticFailIfTrue(*ident == propertyNames.constructor, "Cannot declare class field named 'constructor'");
+
+            if (computedPropertyName) {
+                ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++);
+                DeclarationResultMask declarationResult = classScope->declareLexicalVariable(ident, true);
+                ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid);
+                classScope->useVariable(ident, false);
+                classScope->addClosedVariableCandidateUnconditionally(ident->impl());
+            }
+
+            TreeExpression initializer = 0;
+            if (consume(EQUAL)) {
+                SetForScope<bool> overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, true);
+                classScope->setExpectedSuperBinding(SuperBinding::Needed);
+                initializer = parseAssignmentExpression(context);
+                classScope->setExpectedSuperBinding(SuperBinding::NotNeeded);
+                failIfFalse(initializer, "Cannot parse initializer for class field");
+                classScope->markLastUsedVariablesSetAsCaptured();
+            }
+            failIfFalse(autoSemiColon(), "Expected a ';' following a class field");
+            auto inferName = initializer ? InferName::Allowed : InferName::Disallowed;
+            if (computedPropertyName)
+                property = context.createProperty(ident, computedPropertyName, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, tag);
+            else
+                property = context.createProperty(ident, initializer, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::NotNeeded, inferName, tag);
         } else {
             ParserFunctionInfo<TreeBuilder> methodInfo;
             bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor;
@@ -2971,11 +3013,9 @@ parseMethod:
                 "Cannot declare a static method named 'prototype'");
 
             if (computedPropertyName) {
-                property = context.createProperty(computedPropertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed),
-                    PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, tag);
+                property = context.createProperty(computedPropertyName, method, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, tag);
             } else {
-                property = context.createProperty(methodInfo.name, method, PropertyNode::Constant,
-                    PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, InferName::Allowed, tag);
+                property = context.createProperty(methodInfo.name, method, type, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, InferName::Allowed, tag);
             }
         }
 
@@ -2993,6 +3033,68 @@ parseMethod:
     return classExpression;
 }
 
+template <typename LexerType>
+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstanceFieldInitializerSourceElements(TreeBuilder& context, const Vector<JSTextPosition>& instanceFieldLocations)
+{
+    TreeSourceElements sourceElements = context.createSourceElements();
+    currentScope()->setIsClassScope();
+
+    unsigned numComputedFields = 0;
+    for (auto location : instanceFieldLocations) {
+        // We don't need to worry about hasLineTerminatorBeforeToken
+        // on class fields, so we set this value to false.
+        LexerState lexerState { location.offset, static_cast<unsigned>(location.lineStartOffset), static_cast<unsigned>(location.line), static_cast<unsigned>(location.line), false };
+        restoreLexerState(lexerState);
+
+        JSTokenLocation fieldLocation = tokenLocation();
+        const Identifier* ident = nullptr;
+        TreeExpression computedPropertyName = 0;
+        DefineFieldNode::Type type = DefineFieldNode::Type::Name;
+        switch (m_token.m_type) {
+        case STRING:
+        case IDENT:
+        namedKeyword:
+            ident = m_token.m_data.ident;
+            ASSERT(ident);
+            next();
+            break;
+        case DOUBLE:
+        case INTEGER:
+            ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM&>(m_vm), m_token.m_data.doubleValue);
+            ASSERT(ident);
+            next();
+            break;
+        case OPENBRACKET:
+            next();
+            computedPropertyName = parseAssignmentExpression(context);
+            failIfFalse(computedPropertyName, "Cannot parse computed property name");
+            handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name");
+            ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++);
+            type = DefineFieldNode::Type::ComputedName;
+            break;
+        default:
+            if (m_token.m_type & KeywordTokenFlag)
+                goto namedKeyword;
+            failDueToUnexpectedToken();
+        }
+
+        // Only valid class fields are handled in this function.
+        ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || m_lexer->hasLineTerminatorBeforeToken());
+
+        TreeExpression initializer = 0;
+        if (consume(EQUAL))
+            initializer = parseAssignmentExpression(context);
+
+        TreeStatement defineField = context.createDefineField(fieldLocation, ident, initializer, type);
+        context.appendStatement(sourceElements, defineField);
+    }
+
+    ASSERT(!hasError());
+    // Trick parseInner() into believing we've parsed the entire SourceCode, in order to prevent it from producing an error.
+    m_token.m_type = EOFTOK;
+    return sourceElements;
+}
+
 struct LabelInfo {
     LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end)
     : m_ident(ident)
@@ -4484,9 +4586,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
             const bool isEval = false;
             return createResolveAndUseVariable(context, ident, isEval, start, location);
         }
+        if (UNLIKELY(m_parserState.isParsingClassFieldInitializer))
+            failIfTrue(*m_token.m_data.ident == m_vm.propertyNames->arguments, "Cannot reference 'arguments' in class field initializer");
     identifierExpression:
         JSTextPosition start = tokenStartPosition();
         const Identifier* ident = m_token.m_data.ident;
+        if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext))
+            failIfTrue(*ident == m_vm.propertyNames->arguments, "arguments is not valid in this context");
         JSTokenLocation location(tokenLocation());
         next();
 
@@ -4681,10 +4787,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
         next();
         if (matchContextualKeyword(m_vm.propertyNames->target)) {
             ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope();
-            semanticFailIfFalse(currentScope()->isFunction() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions");
+            ScopeRef classScope = closestClassScopeOrTopLevelScope();
+            bool isClassFieldInitializer = classScope.index() > closestOrdinaryFunctionScope.index();
+            bool isFunctionEvalContextType = closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext;
+            semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is only valid inside functions");
             baseIsNewTarget = true;
             if (currentScope()->isArrowFunction()) {
-                semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is not valid inside arrow functions in global code");
+                semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is not valid inside arrow functions in global code");
                 currentScope()->setInnerArrowFunctionUsesNewTarget();
             }
             ASSERT(lastNewTokenLocation.line);
@@ -4701,9 +4810,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
 
     if (baseIsSuper) {
         ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope();
-        semanticFailIfFalse(currentScope()->isFunction() || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context");
+        ScopeRef classScope = closestClassScopeOrTopLevelScope();
+        bool isClassFieldInitializer = classScope.index() > closestOrdinaryFunctionScope.index();
+        semanticFailIfFalse(currentScope()->isFunction() || isClassFieldInitializer || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context");
         base = context.createSuperExpr(location);
         next();
+        failIfTrue(match(OPENPAREN) && currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext, "super call is not valid in this context");
         ScopeRef functionScope = currentFunctionScope();
         if (!functionScope->setNeedsSuperBinding()) {
             // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error
@@ -4713,7 +4825,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
                 SuperBinding functionSuperBinding = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext()
                     ? functionScope->expectedSuperBinding()
                     : closestOrdinaryFunctionScope->expectedSuperBinding();
-                semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded, "super is not valid in this context");
+                semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded && !isClassFieldInitializer, "super is not valid in this context");
             }
         }
     } else if (baseIsImport) {
@@ -4794,6 +4906,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
                 break;
             }
             case OPENPAREN: {
+                if (baseIsSuper)
+                    failIfTrue(m_parserState.isParsingClassFieldInitializer, "super call is not valid in class field initializer context");
                 m_parserState.nonTrivialExpressionCount++;
                 int nonLHSCount = m_parserState.nonLHSCount;
                 if (newCount) {
index cfd943e..7b72f3b 100644 (file)
@@ -181,6 +181,7 @@ public:
         , m_hasArguments(false)
         , m_isEvalContext(false)
         , m_hasNonSimpleParameterList(false)
+        , m_isClassScope(false)
         , m_evalContextType(EvalContextType::None)
         , m_constructorKind(static_cast<unsigned>(ConstructorKind::None))
         , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded))
@@ -258,6 +259,7 @@ public:
         case SourceParseMode::GetterMode:
         case SourceParseMode::SetterMode:
         case SourceParseMode::MethodMode:
+        case SourceParseMode::InstanceFieldInitializerMode:
             setIsFunction();
             break;
 
@@ -287,6 +289,7 @@ public:
     bool isGeneratorBoundary() const { return m_isGeneratorBoundary; }
     bool isAsyncFunction() const { return m_isAsyncFunction; }
     bool isAsyncFunctionBoundary() const { return m_isAsyncFunctionBoundary; }
+    bool isClassScope() const { return m_isClassScope; }
 
     bool hasArguments() const { return m_hasArguments; }
 
@@ -301,6 +304,12 @@ public:
         m_isLexicalScope = true;
         m_allowsLexicalDeclarations = true;
     }
+
+    void setIsClassScope()
+    {
+        m_isClassScope = true;
+    }
+
     bool isLexicalScope() { return m_isLexicalScope; }
     bool usesEval() { return m_usesEval; }
 
@@ -581,6 +590,12 @@ public:
     {
         m_closedVariableCandidates.add(impl);
     }
+
+    void markLastUsedVariablesSetAsCaptured()
+    {
+        for (UniquedStringImpl* impl : m_usedVariables.last())
+            m_closedVariableCandidates.add(impl);
+    }
     
     void collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables)
     {
@@ -819,6 +834,7 @@ private:
     bool m_hasArguments;
     bool m_isEvalContext;
     bool m_hasNonSimpleParameterList;
+    bool m_isClassScope;
     EvalContextType m_evalContextType;
     unsigned m_constructorKind;
     unsigned m_expectedSuperBinding;
@@ -889,7 +905,7 @@ public:
     ~Parser();
 
     template <class ParsedNode>
-    std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt);
+    std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt, const Vector<JSTextPosition>* = nullptr);
 
     JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); }
     JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); }
@@ -1171,7 +1187,16 @@ private:
         // When reaching the top level scope (it can be non ordinary function scope), we return it.
         return ScopeRef(&m_scopeStack, i);
     }
-    
+
+    ScopeRef closestClassScopeOrTopLevelScope()
+    {
+        unsigned i = m_scopeStack.size() - 1;
+        ASSERT(i < m_scopeStack.size());
+        while (i && !m_scopeStack[i].isClassScope())
+            i--;
+        return ScopeRef(&m_scopeStack, i);
+    }
+
     ScopeRef pushScope()
     {
         bool isFunction = false;
@@ -1347,7 +1372,7 @@ private:
 
     Parser();
 
-    String parseInner(const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt);
+    String parseInner(const Identifier&, SourceParseMode, ParsingContext, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt, const Vector<JSTextPosition>* = nullptr);
 
     void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int);
 
@@ -1576,6 +1601,7 @@ private:
     template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
     template <class TreeBuilder> TreeSourceElements parseAsyncGeneratorFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
     template <class TreeBuilder> TreeSourceElements parseSingleFunction(TreeBuilder&, Optional<int> functionConstructorParametersEndPosition);
+    template <class TreeBuilder> TreeSourceElements parseInstanceFieldInitializerSourceElements(TreeBuilder&, const Vector<JSTextPosition>&);
     template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength);
     template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
     enum class ExportType { Exported, NotExported };
@@ -1762,6 +1788,7 @@ private:
         const Identifier* lastIdentifier { nullptr };
         const Identifier* lastFunctionName { nullptr };
         bool allowAwait { true };
+        bool isParsingClassFieldInitializer { false };
     };
 
     // If you're using this directly, you probably should be using
@@ -1902,7 +1929,7 @@ private:
 
 template <typename LexerType>
 template <class ParsedNode>
-std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition)
+std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional<int> functionConstructorParametersEndPosition, const Vector<JSTextPosition>* instanceFieldLocations)
 {
     int errLine;
     String errMsg;
@@ -1919,7 +1946,7 @@ std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const I
     ASSERT(m_source->startColumn() > OrdinalNumber::beforeFirst());
     unsigned startColumn = m_source->startColumn().zeroBasedInt();
 
-    String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition);
+    String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition, instanceFieldLocations);
 
     int lineNumber = m_lexer->lineNumber();
     bool lexError = m_lexer->sawError();
@@ -2003,7 +2030,8 @@ std::unique_ptr<ParsedNode> parse(
     ConstructorKind defaultConstructorKindForTopLevelFunction = ConstructorKind::None,
     DerivedContextType derivedContextType = DerivedContextType::None,
     EvalContextType evalContextType = EvalContextType::None,
-    DebuggerParseData* debuggerParseData = nullptr)
+    DebuggerParseData* debuggerParseData = nullptr,
+    const Vector<JSTextPosition>* instanceFieldLocations = nullptr)
 {
     ASSERT(!source.provider()->source().isNull());
 
@@ -2014,7 +2042,7 @@ std::unique_ptr<ParsedNode> parse(
     std::unique_ptr<ParsedNode> result;
     if (source.provider()->source().is8Bit()) {
         Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKindForTopLevelFunction, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
-        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
+        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program, WTF::nullopt, instanceFieldLocations);
         if (positionBeforeLastNewline)
             *positionBeforeLastNewline = parser.positionBeforeLastNewline();
         if (builtinMode == JSParserBuiltinMode::Builtin) {
@@ -2027,7 +2055,7 @@ std::unique_ptr<ParsedNode> parse(
     } else {
         ASSERT_WITH_MESSAGE(defaultConstructorKindForTopLevelFunction == ConstructorKind::None, "BuiltinExecutables's special constructors should always use a 8-bit string");
         Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKindForTopLevelFunction, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
-        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
+        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program, WTF::nullopt, instanceFieldLocations);
         if (positionBeforeLastNewline)
             *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     }
index f745029..641312c 100644 (file)
@@ -66,6 +66,7 @@ enum class SourceParseMode : uint8_t {
     AsyncGeneratorWrapperFunctionMode = 16,
     AsyncGeneratorWrapperMethodMode   = 17,
     GeneratorWrapperMethodMode        = 18,
+    InstanceFieldInitializerMode      = 19,
 };
 
 class SourceParseModeSet { 
@@ -114,7 +115,8 @@ ALWAYS_INLINE bool isFunctionParseMode(SourceParseMode parseMode)
         SourceParseMode::AsyncArrowFunctionBodyMode,
         SourceParseMode::AsyncGeneratorBodyMode,
         SourceParseMode::AsyncGeneratorWrapperFunctionMode,
-        SourceParseMode::AsyncGeneratorWrapperMethodMode).contains(parseMode);
+        SourceParseMode::AsyncGeneratorWrapperMethodMode,
+        SourceParseMode::InstanceFieldInitializerMode).contains(parseMode);
 } 
 
 ALWAYS_INLINE bool isAsyncFunctionParseMode(SourceParseMode parseMode) 
index fb82b70..656781c 100644 (file)
@@ -236,6 +236,10 @@ public:
     {
         return Property(type);
     }
+    Property createProperty(const Identifier*, int, int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, ClassElementTag)
+    {
+        return Property(type);
+    }
     int createPropertyList(const JSTokenLocation&, Property) { return PropertyListResult; }
     int createPropertyList(const JSTokenLocation&, Property, int) { return PropertyListResult; }
     int createElementList(int, int) { return ElementsListResult; }
@@ -247,6 +251,7 @@ public:
     int createClauseList(int) { return ClauseListResult; }
     int createClauseList(int, int) { return ClauseListResult; }
     int createFuncDeclStatement(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return StatementResult; }
+    int createDefineField(const JSTokenLocation&, const Identifier*, int, DefineFieldNode::Type) { return 0; }
     int createClassDeclStatement(const JSTokenLocation&, ClassExpression,
         const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; }
     int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&, DeclarationStacks::FunctionStack&&) { return StatementResult; }
index 4e1b801..b3c73cf 100644 (file)
@@ -937,6 +937,7 @@ public:
         m_opProfileControlFlowBytecodeOffsets.encode(encoder, rareData.m_opProfileControlFlowBytecodeOffsets);
         m_bitVectors.encode(encoder, rareData.m_bitVectors);
         m_constantIdentifierSets.encode(encoder, rareData.m_constantIdentifierSets);
+        m_needsClassFieldInitializer = rareData.m_needsClassFieldInitializer;
     }
 
     UnlinkedCodeBlock::RareData* decode(Decoder& decoder) const
@@ -950,6 +951,7 @@ public:
         m_opProfileControlFlowBytecodeOffsets.decode(decoder, rareData->m_opProfileControlFlowBytecodeOffsets);
         m_bitVectors.decode(decoder, rareData->m_bitVectors);
         m_constantIdentifierSets.decode(decoder, rareData->m_constantIdentifierSets);
+        rareData->m_needsClassFieldInitializer = m_needsClassFieldInitializer;
         return rareData;
     }
 
@@ -962,6 +964,7 @@ private:
     CachedVector<InstructionStream::Offset> m_opProfileControlFlowBytecodeOffsets;
     CachedVector<CachedBitVector> m_bitVectors;
     CachedVector<CachedConstantIdentifierSetEntry> m_constantIdentifierSets;
+    unsigned m_needsClassFieldInitializer : 1;
 };
 
 class CachedVariableEnvironment : public CachedObject<VariableEnvironment> {
@@ -1701,6 +1704,7 @@ public:
     unsigned scriptMode() const { return m_scriptMode; }
     unsigned superBinding() const { return m_superBinding; }
     unsigned derivedContextType() const { return m_derivedContextType; }
+    unsigned needsClassFieldInitializer() const { return m_needsClassFieldInitializer; }
 
     Identifier name(Decoder& decoder) const { return m_name.decode(decoder); }
     Identifier ecmaName(Decoder& decoder) const { return m_ecmaName.decode(decoder); }
@@ -1734,6 +1738,7 @@ private:
     unsigned m_constructorKind : 2;
     unsigned m_functionMode : 2; // FunctionMode
     unsigned m_derivedContextType: 2;
+    unsigned m_needsClassFieldInitializer : 1;
 
     CachedPtr<CachedFunctionExecutableRareData> m_rareData;
 
@@ -1786,6 +1791,7 @@ public:
     unsigned isClassContext() const { return m_isClassContext; }
     unsigned constructorKind() const { return m_constructorKind; }
     unsigned derivedContextType() const { return m_derivedContextType; }
+    unsigned needsClassFieldInitializer() const { return m_needsClassFieldInitializer; }
     unsigned evalContextType() const { return m_evalContextType; }
     unsigned hasTailCalls() const { return m_hasTailCalls; }
     unsigned lineCount() const { return m_lineCount; }
@@ -1817,6 +1823,7 @@ private:
     unsigned m_isClassContext : 1;
     unsigned m_constructorKind : 2;
     unsigned m_derivedContextType : 2;
+    unsigned m_needsClassFieldInitializer : 1;
     unsigned m_evalContextType : 2;
     unsigned m_hasTailCalls : 1;
     unsigned m_codeType : 2;
@@ -2095,6 +2102,7 @@ ALWAYS_INLINE void CachedFunctionExecutable::encode(Encoder& encoder, const Unli
     m_scriptMode = executable.m_scriptMode;
     m_superBinding = executable.m_superBinding;
     m_derivedContextType = executable.m_derivedContextType;
+    m_needsClassFieldInitializer = executable.m_needsClassFieldInitializer;
 
     m_rareData.encode(encoder, executable.m_rareData.get());
 
@@ -2142,6 +2150,7 @@ ALWAYS_INLINE UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(Decoder& de
     , m_functionMode(cachedExecutable.functionMode())
     , m_derivedContextType(cachedExecutable.derivedContextType())
     , m_isGeneratedFromCache(true)
+    , m_needsClassFieldInitializer(cachedExecutable.needsClassFieldInitializer())
     , m_unlinkedCodeBlockForCall()
     , m_unlinkedCodeBlockForConstruct()
 
index 4f3c962..ecf9510 100644 (file)
@@ -149,7 +149,7 @@ UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& v
     // The Function constructor only has access to global variables, so no variables will be under TDZ unless they're
     // in the global lexical environment, which we always TDZ check accesses from.
     ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode());
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None);
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, WTF::nullopt, DerivedContextType::None, NeedsClassFieldInitializer::No);
 
     if (!source.provider()->sourceURLDirective().isNull())
         functionExecutable->setSourceURLDirective(source.provider()->sourceURLDirective());
index 4919f00..b4a81e7 100644 (file)
@@ -279,7 +279,10 @@ UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& s
 
     bool usesEval = rootNode->features() & EvalFeature;
     bool isStrictMode = rootNode->features() & StrictModeFeature;
-    ExecutableInfo executableInfo(usesEval, isStrictMode, false, false, ConstructorKind::None, scriptMode, SuperBinding::NotNeeded, CacheTypes<UnlinkedCodeBlockType>::parseMode, derivedContextType, isArrowFunctionContext, false, evalContextType);
+    NeedsClassFieldInitializer needsClassFieldInitializer = NeedsClassFieldInitializer::No;
+    if constexpr (std::is_same_v<ExecutableType, DirectEvalExecutable>)
+        needsClassFieldInitializer = executable->needsClassFieldInitializer();
+    ExecutableInfo executableInfo(usesEval, isStrictMode, false, false, ConstructorKind::None, scriptMode, SuperBinding::NotNeeded, CacheTypes<UnlinkedCodeBlockType>::parseMode, derivedContextType, needsClassFieldInitializer, isArrowFunctionContext, false, evalContextType);
 
     UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(vm, executableInfo, codeGenerationMode);
     unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), lineCount, unlinkedEndColumn);
index 87716d2..875b302 100644 (file)
@@ -1015,6 +1015,13 @@ SLOW_PATH_DECL(slow_path_enter)
     END();
 }
 
+SLOW_PATH_DECL(slow_path_to_property_key)
+{
+    BEGIN();
+    auto bytecode = pc->as<OpToPropertyKey>();
+    RETURN(GET_C(bytecode.m_src).jsValue().toPropertyKeyValue(globalObject));
+}
+
 SLOW_PATH_DECL(slow_path_get_enumerable_length)
 {
     BEGIN();
index 71e785e..228717e 100644 (file)
@@ -367,6 +367,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_in_by_val);
 SLOW_PATH_HIDDEN_DECL(slow_path_del_by_val);
 SLOW_PATH_HIDDEN_DECL(slow_path_strcat);
 SLOW_PATH_HIDDEN_DECL(slow_path_to_primitive);
+SLOW_PATH_HIDDEN_DECL(slow_path_to_property_key);
 SLOW_PATH_HIDDEN_DECL(slow_path_get_enumerable_length);
 SLOW_PATH_HIDDEN_DECL(slow_path_has_generic_property);
 SLOW_PATH_HIDDEN_DECL(slow_path_has_structure_property);
index 6b08518..9ee8c91 100644 (file)
@@ -35,7 +35,7 @@
 
 namespace JSC {
 
-DirectEvalExecutable* DirectEvalExecutable::create(JSGlobalObject* globalObject, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType, const VariableEnvironment* variablesUnderTDZ)
+DirectEvalExecutable* DirectEvalExecutable::create(JSGlobalObject* globalObject, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isArrowFunctionContext, EvalContextType evalContextType, const VariableEnvironment* variablesUnderTDZ)
 {
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
@@ -45,7 +45,7 @@ DirectEvalExecutable* DirectEvalExecutable::create(JSGlobalObject* globalObject,
         return 0;
     }
 
-    auto* executable = new (NotNull, allocateCell<DirectEvalExecutable>(vm.heap)) DirectEvalExecutable(globalObject, source, isInStrictContext, derivedContextType, isArrowFunctionContext, evalContextType);
+    auto* executable = new (NotNull, allocateCell<DirectEvalExecutable>(vm.heap)) DirectEvalExecutable(globalObject, source, isInStrictContext, derivedContextType, needsClassFieldInitializer, isArrowFunctionContext, evalContextType);
     executable->finishCreation(vm);
 
     ParserError error;
@@ -69,9 +69,10 @@ DirectEvalExecutable* DirectEvalExecutable::create(JSGlobalObject* globalObject,
     return executable;
 }
 
-DirectEvalExecutable::DirectEvalExecutable(JSGlobalObject* globalObject, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType)
-    : EvalExecutable(globalObject, source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType)
+DirectEvalExecutable::DirectEvalExecutable(JSGlobalObject* globalObject, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isArrowFunctionContext, EvalContextType evalContextType)
+    : EvalExecutable(globalObject, source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType, needsClassFieldInitializer)
 {
+    ASSERT(needsClassFieldInitializer == NeedsClassFieldInitializer::No || derivedContextType == DerivedContextType::DerivedConstructorContext);
 }
 
 } // namespace JSC
index a08fa56..f7abd5f 100644 (file)
@@ -31,9 +31,9 @@ namespace JSC {
 
 class DirectEvalExecutable final : public EvalExecutable {
 public:
-    static DirectEvalExecutable* create(JSGlobalObject*, const SourceCode&, bool isInStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType, const VariableEnvironment*);
+    static DirectEvalExecutable* create(JSGlobalObject*, const SourceCode&, bool isInStrictContext, DerivedContextType, NeedsClassFieldInitializer, bool isArrowFunctionContext, EvalContextType, const VariableEnvironment*);
 private:
-    DirectEvalExecutable(JSGlobalObject*, const SourceCode&, bool inStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType);
+    DirectEvalExecutable(JSGlobalObject*, const SourceCode&, bool inStrictContext, DerivedContextType, NeedsClassFieldInitializer, bool isArrowFunctionContext, EvalContextType);
 };
 
 static_assert(sizeof(DirectEvalExecutable) == sizeof(EvalExecutable), "");
index 947e27f..5e13809 100644 (file)
@@ -33,8 +33,9 @@ namespace JSC {
 
 const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(EvalExecutable) };
 
-EvalExecutable::EvalExecutable(JSGlobalObject* globalObject, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType)
+EvalExecutable::EvalExecutable(JSGlobalObject* globalObject, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType, NeedsClassFieldInitializer needsClassFieldInitializer)
     : Base(globalObject->vm().evalExecutableStructure.get(), globalObject->vm(), source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType, NoIntrinsic)
+    , m_needsClassFieldInitializer(static_cast<unsigned>(needsClassFieldInitializer))
 {
     ASSERT(source.provider()->sourceType() == SourceProviderSourceType::Program);
 }
index 164445e..79261d9 100644 (file)
@@ -62,13 +62,13 @@ public:
 
     DECLARE_INFO;
 
-    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, evalContextType()); }
+    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), needsClassFieldInitializer(), isArrowFunctionContext(), false, evalContextType()); }
 
     unsigned numVariables() { return m_unlinkedEvalCodeBlock->numVariables(); }
     unsigned numFunctionHoistingCandidates() { return m_unlinkedEvalCodeBlock->numFunctionHoistingCandidates(); }
     unsigned numTopLevelFunctionDecls() { return m_unlinkedEvalCodeBlock->numberOfFunctionDecls(); }
     bool allowDirectEvalCache() const { return m_unlinkedEvalCodeBlock->allowDirectEvalCache(); }
-
+    NeedsClassFieldInitializer needsClassFieldInitializer() const { return static_cast<NeedsClassFieldInitializer>(m_needsClassFieldInitializer); }
     TemplateObjectMap& ensureTemplateObjectMap(VM&);
 
 protected:
@@ -76,10 +76,12 @@ protected:
     friend class ScriptExecutable;
 
     using Base::finishCreation;
-    EvalExecutable(JSGlobalObject*, const SourceCode&, bool inStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType);
+    EvalExecutable(JSGlobalObject*, const SourceCode&, bool inStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType, NeedsClassFieldInitializer);
 
     static void visitChildren(JSCell*, SlotVisitor&);
 
+    unsigned m_needsClassFieldInitializer : 1;
+
     WriteBarrier<ExecutableToCodeBlockEdge> m_evalCodeBlock;
     WriteBarrier<UnlinkedEvalCodeBlock> m_unlinkedEvalCodeBlock;
     std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
index d779bd1..59d1f9c 100644 (file)
@@ -113,6 +113,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(JSGlobalObject* globalObj
             break;
 
         case SourceParseMode::ArrowFunctionMode:
+        case SourceParseMode::InstanceFieldInitializerMode:
             functionHeader = "";
             break;
 
index 51816e5..487d8b8 100644 (file)
@@ -69,7 +69,7 @@ IndirectEvalExecutable* IndirectEvalExecutable::create(JSGlobalObject* globalObj
 }
 
 IndirectEvalExecutable::IndirectEvalExecutable(JSGlobalObject* globalObject, const SourceCode& source, bool inStrictContext, DerivedContextType derivedContextType, bool isArrowFunctionContext, EvalContextType evalContextType)
-    : EvalExecutable(globalObject, source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType)
+    : EvalExecutable(globalObject, source, inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType, NeedsClassFieldInitializer::No)
 {
 }
 
index 2599e2c..fe0180d 100644 (file)
@@ -273,6 +273,7 @@ public:
     JSString* toString(JSGlobalObject*) const; // On exception, this returns the empty string.
     JSString* toStringOrNull(JSGlobalObject*) const; // On exception, this returns null, to make exception checks faster.
     Identifier toPropertyKey(JSGlobalObject*) const;
+    JSValue toPropertyKeyValue(JSGlobalObject*) const;
     WTF::String toWTFString(JSGlobalObject*) const;
     JSObject* toObject(JSGlobalObject*) const;
 
index 7d8645c..5b3ff8a 100644 (file)
@@ -691,6 +691,22 @@ ALWAYS_INLINE Identifier JSValue::toPropertyKey(JSGlobalObject* globalObject) co
     RELEASE_AND_RETURN(scope, string->toIdentifier(globalObject));
 }
 
+ALWAYS_INLINE JSValue JSValue::toPropertyKeyValue(JSGlobalObject* globalObject) const
+{
+    VM& vm = getVM(globalObject);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (isString() || isSymbol())
+        return *this;
+
+    JSValue primitive = toPrimitive(globalObject, PreferString);
+    RETURN_IF_EXCEPTION(scope, JSValue());
+    if (primitive.isSymbol())
+        return primitive;
+
+    RELEASE_AND_RETURN(scope, primitive.toString(globalObject));
+}
+
 inline JSValue JSValue::toPrimitive(JSGlobalObject* globalObject, PreferredPrimitiveType preferredType) const
 {
     return isCell() ? asCell()->toPrimitive(globalObject, preferredType) : asValue();
index 34e6585..cd6ec15 100644 (file)
@@ -429,6 +429,7 @@ EncodedJSValue JSFunction::callerGetter(JSGlobalObject* globalObject, EncodedJSV
     case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
     case SourceParseMode::AsyncGeneratorWrapperMethodMode:
     case SourceParseMode::GeneratorWrapperMethodMode:
+    case SourceParseMode::InstanceFieldInitializerMode:
         if (!function->jsExecutable()->isStrictMode())
             return JSValue::encode(caller);
         return JSValue::encode(throwTypeError(globalObject, scope, "Function.caller used to retrieve strict caller"_s));
index e089268..4654620 100644 (file)
@@ -63,7 +63,7 @@ public:
 
     DECLARE_INFO;
 
-    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); }
+    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Module, SuperBinding::NotNeeded, SourceParseMode::ModuleEvaluateMode, derivedContextType(), NeedsClassFieldInitializer::No, isArrowFunctionContext(), false, EvalContextType::None); }
 
     UnlinkedModuleProgramCodeBlock* unlinkedModuleProgramCodeBlock() { return m_unlinkedModuleProgramCodeBlock.get(); }
 
index 46f0916..f35c5e1 100644 (file)
@@ -496,6 +496,7 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     v(Bool, useUnlinkedCodeBlockJettisoning, false, Normal, "If true, UnlinkedCodeBlock can be jettisoned.") \
     v(Bool, forceOSRExitToLLInt, false, Normal, "If true, we always exit to the LLInt. If false, we exit to whatever is most convenient.") \
     v(Unsigned, getByValICMaxNumberOfIdentifiers, 4, Normal, "Number of identifiers we see in the LLInt that could cause us to bail on generating an IC for get_by_val.") \
+    v(Bool, useClassFields, false, Normal, "If true, the parser will understand data fields inside classes.") \
 
 enum OptionEquivalence {
     SameOption,
index cde281b..29ca013 100644 (file)
@@ -71,7 +71,7 @@ public:
         
     DECLARE_INFO;
 
-    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), isArrowFunctionContext(), false, EvalContextType::None); }
+    ExecutableInfo executableInfo() const { return ExecutableInfo(usesEval(), isStrictMode(), false, false, ConstructorKind::None, JSParserScriptMode::Classic, SuperBinding::NotNeeded, SourceParseMode::ProgramMode, derivedContextType(), NeedsClassFieldInitializer::No, isArrowFunctionContext(), false, EvalContextType::None); }
 
     TemplateObjectMap& ensureTemplateObjectMap(VM&);