From 44d5f1a6a9565ece2cfdffcec0a533b1fe8f91c1 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Tue, 21 May 2019 11:02:01 +0800 Subject: [PATCH] import change from rmottola/Arctic-Fox: - initialize webp with different mode depending on endianness (c47ba6f6e) - pointer style (09a5f12ee) - Bug 1139983 - Fail the alloc if the nursery is full in NoGC (a85408eef) - Bug 1135963. Allow JS_InitializePropertiesFromCompatibleNativeObject to deal with objects that have different prototypes. (ec8d1799b) - Bug 1066233 - Part 1: Parser suppoert for ES6 ClassExpressions. (04e536bb6) - Bug 1066233 - Part 2: Emitter support for ES6 ClassExpressions. (7e141d412) - Bug 1066233 - Part 3: Reflect.parse support for ES6 ClassExpressions. (30595e6df) - Bug 1066233 - Followup: Address a forgotten review comment. (836b26f59) - Bug 1066233 - Tests. (5df23d2e6) - Bug 1143106 - Fix construction of singleton objects during parsing when unboxed objects are in use (1749494c6) --- image/decoders/nsWEBPDecoder.cpp | 4 + js/src/builtin/TestingFunctions.cpp | 6 +- js/src/builtin/TypedObject.cpp | 36 +-- js/src/builtin/TypedObject.h | 12 +- js/src/frontend/BytecodeEmitter.cpp | 62 ++-- js/src/frontend/ParseNode.cpp | 19 +- js/src/frontend/ParseNode.h | 40 ++- js/src/frontend/Parser.cpp | 67 +++-- js/src/frontend/Parser.h | 8 +- js/src/gc/Allocator.cpp | 51 +--- js/src/gc/Allocator.h | 23 -- js/src/jit-test/tests/basic/bug1143106.js | 8 + js/src/jsarray.cpp | 14 +- js/src/jsast.tbl | 1 + js/src/jscompartment.cpp | 22 +- js/src/jscompartment.h | 26 +- js/src/jsfriendapi.cpp | 6 +- js/src/jsfriendapi.h | 10 +- js/src/jsobj.cpp | 32 ++- js/src/jsobjinlines.h | 8 +- js/src/jsreflect.cpp | 43 ++- js/src/tests/ecma_6/Class/classHeritage.js | 66 +++-- js/src/tests/ecma_6/Class/classPrototype.js | 50 +++- .../tests/ecma_6/Class/constructorCalled.js | 20 ++ js/src/tests/ecma_6/Class/innerBinding.js | 57 +++- .../tests/ecma_6/Class/methodInstallation.js | 128 +++++---- js/src/tests/ecma_6/Class/methodOverwrites.js | 102 +++++-- js/src/tests/ecma_6/Class/outerBinding.js | 2 +- js/src/tests/ecma_6/Class/strictExecution.js | 23 +- .../tests/js1_8_5/extensions/reflect-parse.js | 267 +++++++++++------- js/src/vm/ArgumentsObject.cpp | 2 +- js/src/vm/ArrayObject-inl.h | 6 +- js/src/vm/Debugger.h | 8 +- js/src/vm/Runtime-inl.h | 27 +- js/src/vm/SavedStacks.cpp | 6 +- js/src/vm/SavedStacks.h | 6 +- js/src/vm/ScopeObject.cpp | 6 +- js/src/vm/ScopeObject.h | 4 +- js/src/vm/Shape.cpp | 6 +- js/src/vm/Shape.h | 18 +- js/src/vm/UnboxedObject.cpp | 3 + 41 files changed, 793 insertions(+), 512 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug1143106.js diff --git a/image/decoders/nsWEBPDecoder.cpp b/image/decoders/nsWEBPDecoder.cpp index 0ecc7e7135..3cb51baa69 100644 --- a/image/decoders/nsWEBPDecoder.cpp +++ b/image/decoders/nsWEBPDecoder.cpp @@ -44,7 +44,11 @@ nsWEBPDecoder::~nsWEBPDecoder() void nsWEBPDecoder::InitInternal() { +#if MOZ_BIG_ENDIAN + mDecoder = WebPINewRGB(MODE_Argb, nullptr, 0, 0); +#else mDecoder = WebPINewRGB(MODE_rgbA, nullptr, 0, 0); +#endif if (!mDecoder) { PostDecoderError(NS_ERROR_FAILURE); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index cc5d782f4d..aa7b063543 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1390,7 +1390,7 @@ DisplayName(JSContext* cx, unsigned argc, jsval* vp) } static bool -ShellObjectMetadataCallback(JSContext* cx, JSObject** pmetadata) +ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata) { RootedObject obj(cx, NewBuiltinClassInstance(cx)); if (!obj) @@ -1448,7 +1448,7 @@ SetObjectMetadataCallback(JSContext* cx, unsigned argc, jsval* vp) } static bool -SetObjectMetadata(JSContext* cx, unsigned argc, jsval* vp) +SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 2 || !args[0].isObject() || !args[1].isObject()) { @@ -1464,7 +1464,7 @@ SetObjectMetadata(JSContext* cx, unsigned argc, jsval* vp) } static bool -GetObjectMetadata(JSContext* cx, unsigned argc, jsval* vp) +GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 1 || !args[0].isObject()) { diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index b1a4212b50..7623677d02 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1378,10 +1378,10 @@ bool TypedObject::isAttached() const { if (is()) { - LazyArrayBufferTable* table = compartment()->lazyArrayBuffers; + LazyArrayBufferTable *table = compartment()->lazyArrayBuffers; if (table) { - ArrayBufferObject* buffer = - table->maybeBuffer(&const_cast(this)->as()); + ArrayBufferObject *buffer = + table->maybeBuffer(&const_cast(this)->as()); if (buffer) return !buffer->isNeutered(); } @@ -2190,17 +2190,17 @@ InlineTypedObject::objectMovedDuringMinorGC(JSTracer *trc, JSObject *dst, JSObje } } -ArrayBufferObject* -InlineTransparentTypedObject::getOrCreateBuffer(JSContext* cx) +ArrayBufferObject * +InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx) { - LazyArrayBufferTable*& table = cx->compartment()->lazyArrayBuffers; + LazyArrayBufferTable *&table = cx->compartment()->lazyArrayBuffers; if (!table) { table = cx->new_(cx); if (!table) return nullptr; } - ArrayBufferObject* buffer = table->maybeBuffer(this); + ArrayBufferObject *buffer = table->maybeBuffer(this); if (buffer) return buffer; @@ -2232,15 +2232,15 @@ InlineTransparentTypedObject::getOrCreateBuffer(JSContext* cx) return buffer; } -ArrayBufferObject* -OutlineTransparentTypedObject::getOrCreateBuffer(JSContext* cx) +ArrayBufferObject * +OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx) { if (owner().is()) return &owner().as(); return owner().as().getOrCreateBuffer(cx); } -LazyArrayBufferTable::LazyArrayBufferTable(JSContext* cx) +LazyArrayBufferTable::LazyArrayBufferTable(JSContext *cx) : map(cx) { if (!map.init()) @@ -2252,8 +2252,8 @@ LazyArrayBufferTable::~LazyArrayBufferTable() WeakMapBase::removeWeakMapFromList(&map); } -ArrayBufferObject* -LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject* obj) +ArrayBufferObject * +LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject *obj) { if (Map::Ptr p = map.lookup(obj)) return &p->value()->as(); @@ -2261,7 +2261,7 @@ LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject* obj) } bool -LazyArrayBufferTable::addBuffer(JSContext* cx, InlineTransparentTypedObject* obj, ArrayBufferObject* buffer) +LazyArrayBufferTable::addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer) { MOZ_ASSERT(!map.has(obj)); if (!map.put(obj, buffer)) { @@ -2273,12 +2273,12 @@ LazyArrayBufferTable::addBuffer(JSContext* cx, InlineTransparentTypedObject* obj if (IsInsideNursery(obj)) { // Strip the barriers from the type before inserting into the store // buffer, as is done for DebugScopes::proxiedScopes. - Map::Base* baseHashMap = static_cast(&map); + Map::Base *baseHashMap = static_cast(&map); - typedef HashMap UnbarrieredMap; - UnbarrieredMap* unbarrieredMap = reinterpret_cast(baseHashMap); + typedef HashMap UnbarrieredMap; + UnbarrieredMap *unbarrieredMap = reinterpret_cast(baseHashMap); - typedef gc::HashKeyRef Ref; + typedef gc::HashKeyRef Ref; cx->runtime()->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, obj)); // Also make sure the buffer is traced, so that its data pointer is @@ -2290,7 +2290,7 @@ LazyArrayBufferTable::addBuffer(JSContext* cx, InlineTransparentTypedObject* obj } void -LazyArrayBufferTable::trace(JSTracer* trc) +LazyArrayBufferTable::trace(JSTracer *trc) { map.trace(trc); } diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 3c2bdb7cf1..da15c5c0e9 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -1035,18 +1035,18 @@ class LazyArrayBufferTable Map map; public: - explicit LazyArrayBufferTable(JSContext* cx); + explicit LazyArrayBufferTable(JSContext *cx); ~LazyArrayBufferTable(); - ArrayBufferObject* maybeBuffer(InlineTransparentTypedObject* obj); - bool addBuffer(JSContext* cx, InlineTransparentTypedObject* obj, ArrayBufferObject* buffer); + ArrayBufferObject *maybeBuffer(InlineTransparentTypedObject *obj); + bool addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer); - void trace(JSTracer* trc); + void trace(JSTracer *trc); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); }; -JSObject* -InitTypedObjectModuleObject(JSContext* cx, JS::HandleObject obj); +JSObject * +InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj); } // namespace js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index af3282762e..3be950a9c1 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4236,8 +4236,11 @@ EmitAssignment(ExclusiveContext* cx, BytecodeEmitter* bce, ParseNode* lhs, JSOp } bool -ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp) +ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObjects, MutableHandleValue vp, + NewObjectKind newKind) { + MOZ_ASSERT(newKind == TenuredObject || newKind == SingletonObject); + switch (getKind()) { case PNK_NUMBER: vp.setNumber(pn_dval); @@ -4277,8 +4280,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje pn = pn_head; } - RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, count, NullPtr(), - MaybeSingletonObject)); + RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, count, NullPtr(), newKind)); if (!obj) return false; @@ -4341,7 +4343,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje } JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), - TenuredObject); + newKind); if (!obj) return false; @@ -4355,17 +4357,17 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje } static bool -EmitSingletonInitialiser(ExclusiveContext* cx, BytecodeEmitter* bce, ParseNode* pn) +EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) { + NewObjectKind newKind = (pn->getKind() == PNK_OBJECT) ? SingletonObject : TenuredObject; + RootedValue value(cx); - if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value)) + if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, newKind)) return false; - RootedNativeObject obj(cx, &value.toObject().as()); - if (!obj->is() && !JSObject::setSingleton(cx, obj)) - return false; + MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton()); - ObjectBox* objbox = bce->parser->newObjectBox(obj); + ObjectBox *objbox = bce->parser->newObjectBox(&value.toObject()); if (!objbox) return false; @@ -6932,11 +6934,8 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) ClassNames *names = classNode.names(); ParseNode *heritageExpression = classNode.heritage(); - LexicalScopeNode *innerBlock = classNode.scope(); - - ParseNode *classMethods = innerBlock->pn_expr; - MOZ_ASSERT(classMethods->isKind(PNK_CLASSMETHODLIST)); + ParseNode *classMethods = classNode.methodList(); ParseNode *constructor = nullptr; for (ParseNode *mn = classMethods->pn_head; mn; mn = mn->pn_next) { ClassMethod &method = mn->as(); @@ -6953,8 +6952,10 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) bool savedStrictness = bce->sc->setLocalStrictMode(true); StmtInfoBCE stmtInfo(cx); - if (!EnterBlockScope(cx, bce, &stmtInfo, innerBlock->pn_objbox, JSOP_UNINITIALIZED)) - return false; + if (names) { + if (!EnterBlockScope(cx, bce, &stmtInfo, classNode.scopeObject(), JSOP_UNINITIALIZED)) + return false; + } if (heritageExpression) { if (!EmitTree(cx, bce, heritageExpression)) @@ -6992,20 +6993,25 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (Emit1(cx, bce, JSOP_POP) < 0) return false; - // That DEFCONST is never gonna be used, but use it here for logical consistency. - ParseNode *innerName = names->innerBinding(); - if (!EmitLexicalInitialization(cx, bce, innerName, JSOP_DEFCONST)) - return false; + if (names) { + // That DEFCONST is never gonna be used, but use it here for logical consistency. + ParseNode *innerName = names->innerBinding(); + if (!EmitLexicalInitialization(cx, bce, innerName, JSOP_DEFCONST)) + return false; - if (!LeaveNestedScope(cx, bce, &stmtInfo)) - return false; + if (!LeaveNestedScope(cx, bce, &stmtInfo)) + return false; - ParseNode *outerName = names->outerBinding(); - if (!EmitLexicalInitialization(cx, bce, outerName, JSOP_DEFVAR)) - return false; - - if (Emit1(cx, bce, JSOP_POP) < 0) - return false; + ParseNode *outerName = names->outerBinding(); + if (outerName) { + if (!EmitLexicalInitialization(cx, bce, outerName, JSOP_DEFVAR)) + return false; + // Only class statements make outer bindings, and they do not leave + // themselves on the stack. + if (Emit1(cx, bce, JSOP_POP) < 0) + return false; + } + } MOZ_ALWAYS_TRUE(bce->sc->setLocalStrictMode(savedStrictness)); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index af3b66fe54..690b8fadf2 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -269,7 +269,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_WHILE: case PNK_SWITCH: case PNK_LETBLOCK: - case PNK_CLASSNAMES: case PNK_CLASSMETHOD: case PNK_FOR: { MOZ_ASSERT(pn->isArity(PN_BINARY)); @@ -278,6 +277,15 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushResult::Recyclable; } + // Named class expressions do not have outer binding nodes + case PNK_CLASSNAMES: { + MOZ_ASSERT(pn->isArity(PN_BINARY)); + if (pn->pn_left) + stack->push(pn->pn_left); + stack->push(pn->pn_right); + return PushResult::Recyclable; + } + // PNK_WITH is PN_BINARY_OBJ -- that is, PN_BINARY with (irrelevant for // this method's purposes) the addition of the StaticWithObject as // pn_binary_obj. Both left (expression) and right (statement) are @@ -384,10 +392,11 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushResult::Recyclable; } - // classes might have an optinal node for the heritage + // classes might have an optional node for the heritage, as well as the names case PNK_CLASS: { MOZ_ASSERT(pn->isArity(PN_TERNARY)); - stack->push(pn->pn_kid1); + if (pn->pn_kid1) + stack->push(pn->pn_kid1); if (pn->pn_kid2) stack->push(pn->pn_kid2); stack->push(pn->pn_kid3); @@ -1094,7 +1103,7 @@ NameNode::dump(int indent) } #endif -ObjectBox::ObjectBox(NativeObject* object, ObjectBox* traceLink) +ObjectBox::ObjectBox(JSObject *object, ObjectBox* traceLink) : object(object), traceLink(traceLink), emitLink(nullptr) @@ -1102,7 +1111,7 @@ ObjectBox::ObjectBox(NativeObject* object, ObjectBox* traceLink) MOZ_ASSERT(!object->is()); } -ObjectBox::ObjectBox(JSFunction* function, ObjectBox* traceLink) +ObjectBox::ObjectBox(JSFunction *function, ObjectBox* traceLink) : object(function), traceLink(traceLink), emitLink(nullptr) diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 30c1e06e67..1b4aae936e 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -874,7 +874,8 @@ class ParseNode AllowObjects }; - bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp); + bool getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObjects, MutableHandleValue vp, + NewObjectKind newKind = TenuredObject); inline bool isConstant(); template @@ -1362,9 +1363,9 @@ struct ClassNames : public BinaryNode { ClassNames(ParseNode *outerBinding, ParseNode *innerBinding, const TokenPos &pos) : BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding) { - MOZ_ASSERT(outerBinding->isKind(PNK_NAME)); + MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(PNK_NAME)); MOZ_ASSERT(innerBinding->isKind(PNK_NAME)); - MOZ_ASSERT(innerBinding->pn_atom == outerBinding->pn_atom); + MOZ_ASSERT_IF(outerBinding, innerBinding->pn_atom == outerBinding->pn_atom); } static bool test(const ParseNode &node) { @@ -1390,11 +1391,12 @@ struct ClassNames : public BinaryNode { }; struct ClassNode : public TernaryNode { - ClassNode(ParseNode *names, ParseNode *heritage, ParseNode *methodBlock) - : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodBlock) + ClassNode(ParseNode *names, ParseNode *heritage, ParseNode *methodsOrBlock) + : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock) { - MOZ_ASSERT(names->is()); - MOZ_ASSERT(methodBlock->is()); + MOZ_ASSERT_IF(names, names->is()); + MOZ_ASSERT(methodsOrBlock->is() || + methodsOrBlock->isKind(PNK_CLASSMETHODLIST)); } static bool test(const ParseNode &node) { @@ -1404,13 +1406,23 @@ struct ClassNode : public TernaryNode { } ClassNames *names() const { - return &pn_kid1->as(); + return pn_kid1 ? &pn_kid1->as() : nullptr; } ParseNode *heritage() const { return pn_kid2; } - LexicalScopeNode *scope() const { - return &pn_kid3->as(); + ParseNode *methodList() const { + if (pn_kid3->isKind(PNK_CLASSMETHODLIST)) + return pn_kid3; + + MOZ_ASSERT(pn_kid3->is()); + ParseNode *list = pn_kid3->pn_expr; + MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST)); + return list; + } + ObjectBox *scopeObject() const { + MOZ_ASSERT(pn_kid3->is()); + return pn_kid3->pn_objbox; } }; @@ -1625,12 +1637,12 @@ ParseNode::isConstant() class ObjectBox { public: - NativeObject* object; + JSObject *object; - ObjectBox(NativeObject* object, ObjectBox* traceLink); + ObjectBox(JSObject *object, ObjectBox *traceLink); bool isFunctionBox() { return object->is(); } - FunctionBox* asFunctionBox(); - void trace(JSTracer* trc); + FunctionBox *asFunctionBox(); + void trace(JSTracer *trc); protected: friend struct CGObjectList; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 22979dff35..c978849e02 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -564,8 +564,8 @@ Parser::~Parser() } template -ObjectBox* -Parser::newObjectBox(NativeObject* obj) +ObjectBox * +Parser::newObjectBox(JSObject *obj) { MOZ_ASSERT(obj); @@ -5639,10 +5639,12 @@ Parser::debuggerStatement() template <> ParseNode * -Parser::classStatement() +Parser::classDefinition(ClassContext classContext) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + bool savedStrictness = setLocalStrictMode(true); + TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); @@ -5654,10 +5656,13 @@ Parser::classStatement() if (!checkYieldNameValidity()) return null(); name = tokenStream.currentName(); - } else { + } else if (classContext == ClassStatement) { // Class statements must have a bound name report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT); return null(); + } else { + // Make sure to put it back, whatever it was + tokenStream.ungetToken(); } if (name == context->names().let) { @@ -5665,12 +5670,13 @@ Parser::classStatement() return null(); } - bool savedStrictness = setLocalStrictMode(true); - + ParseNode *classBlock = null(); StmtInfoPC classStmt(context); - ParseNode *classBlock = pushLexicalScope(&classStmt); - if (!classBlock) - return null(); + if (name) { + classBlock = pushLexicalScope(&classStmt); + if (!classBlock) + return null(); + } // Because the binding definitions keep track of their blockId, we need to // create at least the inner binding later. Keep track of the name's position @@ -5694,32 +5700,42 @@ Parser::classStatement() ParseNode *classMethods = propertyList(ClassBody); if (!classMethods) return null(); - handler.setLexicalScopeBody(classBlock, classMethods); - ParseNode *innerBinding = makeInitializedLexicalBinding(name, true, namePos); - if (!innerBinding) - return null(); + ParseNode *nameNode = null(); + ParseNode *methodsOrBlock = classMethods; + if (name) { + ParseNode *innerBinding = makeInitializedLexicalBinding(name, true, namePos); + if (!innerBinding) + return null(); - PopStatementPC(tokenStream, pc); + MOZ_ASSERT(classBlock); + handler.setLexicalScopeBody(classBlock, classMethods); + methodsOrBlock = classBlock; - ParseNode *outerBinding = makeInitializedLexicalBinding(name, false, namePos); - if (!outerBinding) - return null(); + PopStatementPC(tokenStream, pc); - ParseNode *nameNode = handler.newClassNames(outerBinding, innerBinding, namePos); - if (!nameNode) - return null(); + ParseNode *outerBinding = null(); + if (classContext == ClassStatement) { + outerBinding = makeInitializedLexicalBinding(name, false, namePos); + if (!outerBinding) + return null(); + } + + nameNode = handler.newClassNames(outerBinding, innerBinding, namePos); + if (!nameNode) + return null(); + } MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); - return handler.newClass(nameNode, classHeritage, classBlock); + return handler.newClass(nameNode, classHeritage, methodsOrBlock); } template <> SyntaxParseHandler::Node -Parser::classStatement() +Parser::classDefinition(ClassContext classContext) { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } @@ -5796,7 +5812,7 @@ Parser::statement(bool canHaveDirectives) case TOK_CLASS: if (!abortIfSyntaxParser()) return null(); - return classStatement(); + return classDefinition(ClassStatement); /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ case TOK_CATCH: @@ -8142,6 +8158,9 @@ Parser::primaryExpr(TokenKind tt, InvokedPrediction invoked) case TOK_FUNCTION: return functionExpr(invoked); + case TOK_CLASS: + return classDefinition(ClassExpression); + case TOK_LB: return arrayInitializer(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index e38c31630a..bc4e24adfe 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -431,8 +431,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter * Allocate a new parsed object or function container from * cx->tempLifoAlloc. */ - ObjectBox* newObjectBox(NativeObject* obj); - FunctionBox* newFunctionBox(Node fn, JSFunction* fun, ParseContext* pc, + ObjectBox *newObjectBox(JSObject *obj); + FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext *pc, Directives directives, GeneratorKind generatorKind); /* @@ -552,7 +552,6 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node throwStatement(); Node tryStatement(); Node debuggerStatement(); - Node classStatement(); Node lexicalDeclaration(bool isConst); Node importDeclaration(); @@ -615,6 +614,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node destructuringExpr(BindData* data, TokenKind tt); Node destructuringExprWithoutYield(BindData* data, TokenKind tt, unsigned msg); + enum ClassContext { ClassStatement, ClassExpression }; + Node classDefinition(ClassContext classContext); + Node identifierName(); bool matchLabel(MutableHandle label); diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index b60859a4df..21c4644c45 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -39,6 +39,7 @@ TryNewNurseryObject(JSContext *cx, size_t thingSize, size_t nDynamicSlots, const JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots, clasp); if (obj) return obj; + if (allowGC && !rt->mainThread.suppressGC) { cx->minorGC(JS::gcreason::OUT_OF_NURSERY); @@ -173,6 +174,14 @@ js::Allocate(ExclusiveContext *cx, AllocKind kind, size_t nDynamicSlots, Initial clasp); if (obj) return obj; + + // Our most common non-jit allocation path is NoGC; thus, if we fail the + // alloc and cannot GC, we *must* return nullptr here so that the caller + // will do a CanGC allocation to clear the nursery. Failing to do so will + // cause all allocations on this path to land in Tenured, and we will not + // get the benefit of the nursery. + if (!allowGC) + return nullptr; } HeapSlot *slots = nullptr; @@ -245,45 +254,3 @@ js::Allocate(ExclusiveContext *cx) template type *js::Allocate(ExclusiveContext *cx); FOR_ALL_NON_OBJECT_GC_LAYOUTS(DECL_ALLOCATOR_INSTANCES) #undef DECL_ALLOCATOR_INSTANCES - -/* - * When allocating for initialization from a cached object copy, we will - * potentially destroy the cache entry we want to copy if we allow GC. On the - * other hand, since these allocations are extremely common, we don't want to - * delay GC from these allocation sites. Instead we allow the GC, but still - * fail the allocation, forcing the non-cached path. - */ -template -NativeObject * -js::gc::AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap, - const js::Class *clasp) -{ - MOZ_ASSERT(clasp->isNative()); - - if (ShouldNurseryAllocateObject(cx->nursery(), heap)) { - size_t thingSize = Arena::thingSize(kind); - - MOZ_ASSERT(thingSize == Arena::thingSize(kind)); - if (!CheckAllocatorState(cx, kind)) - return nullptr; - - JSObject *obj = TryNewNurseryObject(cx, thingSize, 0, clasp); - if (!obj && allowGC) { - cx->minorGC(JS::gcreason::OUT_OF_NURSERY); - return nullptr; - } - return reinterpret_cast(obj); - } - - JSObject *obj = Allocate(cx, kind, 0, heap, clasp); - if (!obj && allowGC) { - cx->runtime()->gc.maybeGC(cx->zone()); - return nullptr; - } - - return reinterpret_cast(obj); -} -template NativeObject *js::gc::AllocateObjectForCacheHit(JSContext *, AllocKind, InitialHeap, - const Class *); -template NativeObject *js::gc::AllocateObjectForCacheHit(JSContext *, AllocKind, InitialHeap, - const Class *); diff --git a/js/src/gc/Allocator.h b/js/src/gc/Allocator.h index d094d3a82f..19a54615ed 100644 --- a/js/src/gc/Allocator.h +++ b/js/src/gc/Allocator.h @@ -10,24 +10,8 @@ #include "gc/Heap.h" #include "js/RootingAPI.h" -namespace JS { -class Symbol; -} // namespace JS -class JSExternalString; -class JSFatInlineString; -class JSObject; -class JSScript; -class JSString; - namespace js { struct Class; -class BaseShape; -class LazyScript; -class ObjectGroup; -class Shape; -namespace jit { -class JitCode; -} // namespace jit template JSObject * @@ -38,13 +22,6 @@ template T * Allocate(ExclusiveContext *cx); -namespace gc { - -template -NativeObject * -AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap, const Class *clasp); - -} // namespace gc } // namespace js #endif // gc_Allocator_h diff --git a/js/src/jit-test/tests/basic/bug1143106.js b/js/src/jit-test/tests/basic/bug1143106.js new file mode 100644 index 0000000000..6bf81bb654 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1143106.js @@ -0,0 +1,8 @@ +({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), +({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), +({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), ({x:3}), +({x:3}), ({x:3}), ({x:3}) + +[{x:3},{x:3},{x:3},{x:3},{x:3},{x:3},{x:3}, + {x:3},{x:3},{x:3},{x:3},{x:3},{x:3},{x:3}, + {x:3},{x:3},{x:3},{x:3},{x:3},{x:3},{x:3}] diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 5091ca8120..d78e9c3e8c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3220,7 +3220,7 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key) if (!group) return nullptr; - JSObject* metadata = nullptr; + JSObject *metadata = nullptr; if (!NewObjectMetadata(cx, &metadata)) return nullptr; @@ -3324,7 +3324,7 @@ EnsureNewArrayElements(ExclusiveContext* cx, ArrayObject* obj, uint32_t length) } static bool -NewArrayIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind) +NewArrayIsCachable(ExclusiveContext *cxArg, NewObjectKind newKind) { return cxArg->isJSContext() && newKind == GenericObject && @@ -3373,7 +3373,7 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, if (!group) return nullptr; - JSObject* metadata = nullptr; + JSObject *metadata = nullptr; if (!NewObjectMetadata(cxArg, &metadata)) return nullptr; @@ -3547,14 +3547,14 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb return arr; } -JSObject* -js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap) +JSObject * +js::NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject templateObject, gc::InitialHeap heap) { RootedShape shape(cx, templateObject->lastProperty()); MOZ_ASSERT(!gc::IsInsideNursery(templateObject)); - JSObject* metadata = nullptr; + JSObject *metadata = nullptr; if (!NewObjectMetadata(cx, &metadata)) return nullptr; if (metadata) { @@ -3563,7 +3563,7 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc return nullptr; } - ArrayObject* arr = ArrayObject::createCopyOnWriteArray(cx, heap, shape, templateObject); + ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, shape, templateObject); if (!arr) return nullptr; diff --git a/js/src/jsast.tbl b/js/src/jsast.tbl index 245e9b0dcf..56545c7469 100644 --- a/js/src/jsast.tbl +++ b/js/src/jsast.tbl @@ -37,6 +37,7 @@ ASTDEF(AST_THIS_EXPR, "ThisExpression", "thisExpress ASTDEF(AST_COMP_EXPR, "ComprehensionExpression", "comprehensionExpression") ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression", "generatorExpression") ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpression") +ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression") ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement") ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement") diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 57450d96d2..b24a88c698 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -689,7 +689,7 @@ JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback) } static bool -AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions) +AddInnerLazyFunctionsFromScript(JSScript *script, AutoObjectVector& lazyFunctions) { if (!script->hasObjects()) return true; @@ -803,16 +803,16 @@ JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, HandleObject h void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t* tiAllocationSiteTables, - size_t* tiArrayTypeTables, - size_t* tiObjectTypeTables, - size_t* compartmentObject, - size_t* compartmentTables, - size_t* innerViewsArg, - size_t* lazyArrayBuffersArg, - size_t* crossCompartmentWrappersArg, - size_t* regexpCompartment, - size_t* savedStacksSet) + size_t *tiAllocationSiteTables, + size_t *tiArrayTypeTables, + size_t *tiObjectTypeTables, + size_t *compartmentObject, + size_t *compartmentTables, + size_t *innerViewsArg, + size_t *lazyArrayBuffersArg, + size_t *crossCompartmentWrappersArg, + size_t *regexpCompartment, + size_t *savedStacksSet) { *compartmentObject += mallocSizeOf(this); objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 2915ba1b24..1a36def0e4 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -250,16 +250,16 @@ struct JSCompartment public: void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t* tiAllocationSiteTables, - size_t* tiArrayTypeTables, - size_t* tiObjectTypeTables, - size_t* compartmentObject, - size_t* compartmentTables, - size_t* innerViews, - size_t* lazyArrayBuffers, - size_t* crossCompartmentWrappers, - size_t* regexpCompartment, - size_t* savedStacksSet); + size_t *tiAllocationSiteTables, + size_t *tiArrayTypeTables, + size_t *tiObjectTypeTables, + size_t *compartmentObject, + size_t *compartmentTables, + size_t *innerViews, + size_t *lazyArrayBuffers, + size_t *crossCompartmentWrappers, + size_t *regexpCompartment, + size_t *savedStacksSet); /* * Shared scope property tree, and arena-pool for allocating its nodes. @@ -293,7 +293,7 @@ struct JSCompartment js::InnerViewTable innerViews; // Map from typed objects to array buffers lazily created for them. - js::LazyArrayBufferTable* lazyArrayBuffers; + js::LazyArrayBufferTable *lazyArrayBuffers; // All unboxed layouts in the compartment. mozilla::LinkedList unboxedLayouts; @@ -399,10 +399,10 @@ struct JSCompartment void forgetObjectMetadataCallback() { objectMetadataCallback = nullptr; } - bool callObjectMetadataCallback(JSContext* cx, JSObject** obj) const { + bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const { return objectMetadataCallback(cx, obj); } - const void* addressOfMetadataCallback() const { + const void *addressOfMetadataCallback() const { return &objectMetadataCallback; } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 1c97c5072e..79c610669c 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1168,13 +1168,13 @@ js::SetObjectMetadataCallback(JSContext* cx, ObjectMetadataCallback callback) } JS_FRIEND_API(bool) -js::SetObjectMetadata(JSContext* cx, HandleObject obj, HandleObject metadata) +js::SetObjectMetadata(JSContext *cx, HandleObject obj, HandleObject metadata) { return JSObject::setMetadata(cx, obj, metadata); } -JS_FRIEND_API(JSObject*) -js::GetObjectMetadata(JSObject* obj) +JS_FRIEND_API(JSObject *) +js::GetObjectMetadata(JSObject *obj) { return obj->getMetadata(); } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 14accdd4dd..51e41ed7bc 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2563,7 +2563,7 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) { }; typedef bool -(* ObjectMetadataCallback)(JSContext* cx, JSObject** pmetadata); +(* ObjectMetadataCallback)(JSContext *cx, JSObject **pmetadata); /* * Specify a callback to invoke when creating each JS object in the current @@ -2572,15 +2572,15 @@ typedef bool * so for efficiency, objects should generally try to share metadata objects. */ JS_FRIEND_API(void) -SetObjectMetadataCallback(JSContext* cx, ObjectMetadataCallback callback); +SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback); /* Manipulate the metadata associated with an object. */ JS_FRIEND_API(bool) -SetObjectMetadata(JSContext* cx, JS::HandleObject obj, JS::HandleObject metadata); +SetObjectMetadata(JSContext *cx, JS::HandleObject obj, JS::HandleObject metadata); -JS_FRIEND_API(JSObject*) -GetObjectMetadata(JSObject* obj); +JS_FRIEND_API(JSObject *) +GetObjectMetadata(JSObject *obj); JS_FRIEND_API(bool) GetElementsWithAdder(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index bc838ce4c6..1862620d98 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1883,10 +1883,10 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx, { assertSameCompartment(cx, src, dst); MOZ_ASSERT(src->getClass() == dst->getClass()); - MOZ_ASSERT(src->getProto() == dst->getProto()); MOZ_ASSERT(dst->lastProperty()->getObjectFlags() == 0); MOZ_ASSERT(!src->getMetadata()); MOZ_ASSERT(!src->isSingleton()); + MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots()); // Save the dst metadata, if any, before we start messing with its shape. RootedObject dstMetadata(cx, dst->getMetadata()); @@ -1901,7 +1901,35 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx, } MOZ_ASSERT(!src->hasPrivate()); - RootedShape shape(cx, src->lastProperty()); + RootedShape shape(cx); + if (src->getProto() == dst->getProto()) { + shape = src->lastProperty(); + } else { + // We need to generate a new shape for dst that has dst's proto but all + // the property information from src. Note that we asserted above that + // dst's object flags are 0 and we plan to set up the metadata later, so + // it's OK to pass null for the metadata here. + shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(), + nullptr, dst->numFixedSlots(), 0); + if (!shape) + return false; + + // Get an in-order list of the shapes in the src object. + AutoShapeVector shapes(cx); + for (Shape::Range r(src->lastProperty()); !r.empty(); r.popFront()) { + if (!shapes.append(&r.front())) + return false; + } + Reverse(shapes.begin(), shapes.end()); + + for (size_t i = 0; i < shapes.length(); i++) { + StackShape unrootedChild(shapes[i]); + RootedGeneric child(cx, &unrootedChild); + shape = cx->compartment()->propertyTree.getChild(cx, shape, *child); + if (!shape) + return false; + } + } size_t span = shape->slotSpan(); if (!dst->setLastProperty(cx, shape)) return false; diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 00441e7e4f..8382355b76 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -235,8 +235,8 @@ ClassCanHaveFixedData(const Class* clasp) } // namespace js -/* static */ inline JSObject* -JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, +/* static */ inline JSObject * +JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, js::HandleShape shape, js::HandleObjectGroup group) { MOZ_ASSERT(shape && group); @@ -829,13 +829,13 @@ Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) } static MOZ_ALWAYS_INLINE bool -NewObjectMetadata(ExclusiveContext* cxArg, JSObject** pmetadata) +NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata) { // The metadata callback is invoked before each created object, except when // analysis/compilation is active, to avoid recursion. It is also skipped // when we allocate objects during a bailout, to prevent stack iterations. MOZ_ASSERT(!*pmetadata); - if (JSContext* cx = cxArg->maybeJSContext()) { + if (JSContext *cx = cxArg->maybeJSContext()) { if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) && !cx->zone()->types.activeAnalysis && !cx->zone()->usedByExclusiveThread) diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index cc10d1ace3..9289e13001 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -618,7 +618,8 @@ class NodeBuilder bool exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst); - bool classStatement(HandleValue name, HandleValue heritage, HandleValue block, TokenPos *pos, MutableHandleValue dst); + bool classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block, TokenPos *pos, + MutableHandleValue dst); bool classMethods(NodeVector &methods, MutableHandleValue dst); bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic, TokenPos *pos, MutableHandleValue dst); @@ -1733,14 +1734,15 @@ NodeBuilder::classMethods(NodeVector &methods, MutableHandleValue dst) } bool -NodeBuilder::classStatement(HandleValue name, HandleValue heritage, HandleValue block, - TokenPos *pos, MutableHandleValue dst) +NodeBuilder::classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block, + TokenPos *pos, MutableHandleValue dst) { - RootedValue cb(cx, callbacks[AST_CLASS_STMT]); + ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT; + RootedValue cb(cx, callbacks[type]); if (!cb.isNull()) return callback(cb, name, heritage, block, pos, dst); - return newNode(AST_CLASS_STMT, pos, + return newNode(type, pos, "name", name, "heritage", heritage, "body", block, @@ -1786,6 +1788,7 @@ class ASTSerializer bool importSpecifier(ParseNode* pn, MutableHandleValue dst); bool exportDeclaration(ParseNode* pn, MutableHandleValue dst); bool exportSpecifier(ParseNode* pn, MutableHandleValue dst); + bool classDefinition(ParseNode* pn, bool expr, MutableHandleValue dst); bool optStatement(ParseNode* pn, MutableHandleValue dst) { if (!pn) { @@ -2395,6 +2398,23 @@ ASTSerializer::forIn(ParseNode* loop, ParseNode* head, HandleValue var, HandleVa builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst); } +bool +ASTSerializer::classDefinition(ParseNode *pn, bool expr, MutableHandleValue dst) +{ + RootedValue className(cx, MagicValue(JS_SERIALIZE_NO_NODE)); + RootedValue heritage(cx); + RootedValue classBody(cx); + + if (pn->pn_kid1) { + if (!identifier(pn->pn_kid1->as().innerBinding(), &className)) + return false; + } + + return optExpression(pn->pn_kid2, &heritage) && + statement(pn->pn_kid3, &classBody) && + builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst); +} + bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { @@ -2603,15 +2623,7 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) return builder.debuggerStatement(&pn->pn_pos, dst); case PNK_CLASS: - { - RootedValue className(cx); - RootedValue heritage(cx); - RootedValue classBody(cx); - return identifier(pn->pn_kid1->as().innerBinding(), &className) && - optExpression(pn->pn_kid2, &heritage) && - statement(pn->pn_kid3, &classBody) && - builder.classStatement(className, heritage, classBody, &pn->pn_pos, dst); - } + return classDefinition(pn, false, dst); case PNK_CLASSMETHODLIST: { @@ -3193,6 +3205,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) LOCAL_ASSERT(pn->pn_count == 1); return comprehension(pn->pn_head, dst); + case PNK_CLASS: + return classDefinition(pn, true, dst); + default: LOCAL_NOT_REACHED("unexpected expression type"); } diff --git a/js/src/tests/ecma_6/Class/classHeritage.js b/js/src/tests/ecma_6/Class/classHeritage.js index 406174647b..d8804bdfbd 100644 --- a/js/src/tests/ecma_6/Class/classHeritage.js +++ b/js/src/tests/ecma_6/Class/classHeritage.js @@ -4,6 +4,9 @@ var test = ` assertThrowsInstanceOf(() => eval(\`class a extends Math.sin { constructor() { } }\`), TypeError); +assertThrowsInstanceOf(() => eval(\`(class a extends Math.sin { + constructor() { } + })\`), TypeError); // Unless it's null, in which case it works like a normal class, except that // the prototype object does not inherit from Object.prototype. @@ -13,14 +16,19 @@ class basic { class nullExtends extends null { constructor() { } } -assertEq(Object.getPrototypeOf(basic), Function.prototype); -assertEq(Object.getPrototypeOf(nullExtends), Function.prototype); -assertEq(Object.getPrototypeOf(basic.prototype), Object.prototype); -assertEq(Object.getPrototypeOf(nullExtends.prototype), null); +var nullExtendsExpr = class extends null { constructor() { } }; -var baseMethodCalled = false; -var staticMethodCalled = false; -var overrideCalled = ""; +assertEq(Object.getPrototypeOf(basic), Function.prototype); +assertEq(Object.getPrototypeOf(basic.prototype), Object.prototype); + +for (let extension of [nullExtends, nullExtendsExpr]) { + assertEq(Object.getPrototypeOf(extension), Function.prototype); + assertEq(Object.getPrototypeOf(extension.prototype), null); +} + +var baseMethodCalled; +var staticMethodCalled; +var overrideCalled; class base { constructor() { }; method() { baseMethodCalled = true; } @@ -31,22 +39,32 @@ class derived extends base { constructor() { }; override() { overrideCalled = "derived"; } } +var derivedExpr = class extends base { + constructor() { }; + override() { overrideCalled = "derived"; } +}; // Make sure we get the right object layouts. -assertEq(Object.getPrototypeOf(derived), base); -assertEq(Object.getPrototypeOf(derived.prototype), base.prototype); +for (let extension of [derived, derivedExpr]) { + baseMethodCalled = false; + staticMethodCalled = false; + overrideCalled = ""; + // Make sure we get the right object layouts. + assertEq(Object.getPrototypeOf(extension), base); + assertEq(Object.getPrototypeOf(extension.prototype), base.prototype); -// We do inherit the methods, right? -(new derived()).method(); -assertEq(baseMethodCalled, true); + // We do inherit the methods, right? + (new extension()).method(); + assertEq(baseMethodCalled, true); -// But we can still override them? -(new derived()).override(); -assertEq(overrideCalled, "derived"); + // But we can still override them? + (new extension()).override(); + assertEq(overrideCalled, "derived"); -// What about the statics? -derived.staticMethod(); -assertEq(staticMethodCalled, true); + // What about the statics? + extension.staticMethod(); + assertEq(staticMethodCalled, true); +} // Gotta extend an object, or null. function nope() { @@ -54,7 +72,13 @@ function nope() { constructor() { } } } +function nopeExpr() { + (class extends "Bar" { + constructor() { } + }); +} assertThrowsInstanceOf(nope, TypeError); +assertThrowsInstanceOf(nopeExpr, TypeError); // The .prototype of the extension must be an object, or null. nope.prototype = "not really, no"; @@ -63,7 +87,13 @@ function stillNo() { constructor() { } } } +function stillNoExpr() { + (class extends nope { + constructor() { } + }); +} assertThrowsInstanceOf(stillNo, TypeError); +assertThrowsInstanceOf(stillNoExpr, TypeError); `; diff --git a/js/src/tests/ecma_6/Class/classPrototype.js b/js/src/tests/ecma_6/Class/classPrototype.js index f6795d0700..3268470bf8 100644 --- a/js/src/tests/ecma_6/Class/classPrototype.js +++ b/js/src/tests/ecma_6/Class/classPrototype.js @@ -2,22 +2,25 @@ var test = ` // The prototype of a class is a non-writable, non-configurable, non-enumerable data property. class a { constructor() { } } -var protoDesc = Object.getOwnPropertyDescriptor(a, "prototype"); -assertEq(protoDesc.writable, false); -assertEq(protoDesc.configurable, false); -assertEq(protoDesc.enumerable, false); +let b = class { constructor() { } }; +for (let test of [a,b]) { + var protoDesc = Object.getOwnPropertyDescriptor(test, "prototype"); + assertEq(protoDesc.writable, false); + assertEq(protoDesc.configurable, false); + assertEq(protoDesc.enumerable, false); -var prototype = protoDesc.value; -assertEq(typeof prototype, "object"); -assertEq(Object.getPrototypeOf(prototype), Object.prototype); -assertEq(Object.isExtensible(prototype), true); + var prototype = protoDesc.value; + assertEq(typeof prototype, "object"); + assertEq(Object.getPrototypeOf(prototype), Object.prototype); + assertEq(Object.isExtensible(prototype), true); -var desiredPrototype = {}; -Object.defineProperty(desiredPrototype, "constructor", { writable: true, - configurable: true, - enumerable: false, - value: a }); -assertDeepEq(prototype, desiredPrototype); + var desiredPrototype = {}; + Object.defineProperty(desiredPrototype, "constructor", { writable: true, + configurable: true, + enumerable: false, + value: test }); + assertDeepEq(prototype, desiredPrototype); +} try { eval(\`class a { @@ -50,6 +53,25 @@ assertThrowsInstanceOf(() => eval(\` static set ["prototype"](x) { } } \`), TypeError); + +assertThrowsInstanceOf(() => eval(\`( + class a { + constructor() { }; + static ["prototype"]() { } + } + )\`), TypeError); +assertThrowsInstanceOf(() => eval(\`( + class a { + constructor() { }; + static get ["prototype"]() { } + } + )\`), TypeError); +assertThrowsInstanceOf(() => eval(\`( + class a { + constructor() { }; + static set ["prototype"](x) { } + } + )\`), TypeError); */ } `; diff --git a/js/src/tests/ecma_6/Class/constructorCalled.js b/js/src/tests/ecma_6/Class/constructorCalled.js index bf77362e2d..b57e38e446 100644 --- a/js/src/tests/ecma_6/Class/constructorCalled.js +++ b/js/src/tests/ecma_6/Class/constructorCalled.js @@ -8,20 +8,40 @@ class a { constructor(x) { assertEq(x, 4); called = true } } new a(4); assertEq(called, true); +called = false; +var aExpr = class { constructor(x) { assertEq(x, 4); called = true } }; +new aExpr(4); +assertEq(called, true); + called = false; class b { constructor() { called = true } method() { } } new b(); assertEq(called, true); +called = false; +var bExpr = class { constructor() { called = true } method() { } }; +new bExpr(); +assertEq(called, true); + called = false; class c { method() { } constructor() { called = true; } } new c(); assertEq(called, true); +called = false; +var cExpr = class { method() { } constructor() { called = true; } } +new cExpr(); +assertEq(called, true); + called = false; class d { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } } new d(); assertEq(called, true); + +called = false; +var dExpr = class { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } } +new dExpr(); +assertEq(called, true); `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/Class/innerBinding.js b/js/src/tests/ecma_6/Class/innerBinding.js index 55d1ffca3f..30985fd37d 100644 --- a/js/src/tests/ecma_6/Class/innerBinding.js +++ b/js/src/tests/ecma_6/Class/innerBinding.js @@ -1,23 +1,36 @@ -// Class statements should create an immutable inner binding. Since all code in -// classes is in strict mode, attempts to mutate it should throw. +// Named class definitions should create an immutable inner binding. +// Since all code in classes is in strict mode, attempts to mutate it +// should throw. if (classesEnabled()) { -// XXXefaust Because we currently try to do assignment to const as an early error, -// this is a syntax error. It is specced to be a TypeError +// XXXefaust Because we currently try to do assignment to const as an early +// error, sometimes, maybe, this is almost sometimes a syntax error. +// It is specced to be a TypeError -function syntaxWrapper() { +function statementWrapper() { eval("class Foo { constructor() { } tryBreak() { Foo = 4; } }"); } -assertThrowsInstanceOf(syntaxWrapper, SyntaxError); + +function expressionWrapper() { + // Mmmmm. Lazy parseing means we don't see this as an error until later. + eval(`var x = class Foo { constructor() { }; tryBreak() { Foo = 4; } }; + new x().tryBreak();`); +} + +assertThrowsInstanceOf(statementWrapper, SyntaxError); +assertThrowsInstanceOf(expressionWrapper, TypeError); + /* var test = ` class Foo { constructor() { }; tryBreak() { Foo = 4; } } -assertThrowsInstanceOf(() => new Foo().tryBreak(), TypeError); +for (let result of [Foo, class Bar { constructor() { }; tryBreak() { Bar = 4; } }]) + assertThrowsInstanceOf(() => new result().tryBreak(), TypeError); { class foo { constructor() { }; tryBreak() { foo = 4; } } - assertThrowsInstanceOf(() => new foo().tryBreak(), TypeError); + for (let result of [foo, class Bar { constructor() { }; tryBreak() { Bar = 4 }]) + assertThrowsInstanceOf(() => new result().tryBreak(), TypeError); } `; */ @@ -30,6 +43,11 @@ assertThrowsInstanceOf(()=>eval(\`class Bar { [Bar] () { }; }\`), ReferenceError); +assertThrowsInstanceOf(()=>eval(\`(class Bar { + constructor() { }; + [Bar] () { }; + })\`), ReferenceError); + // There's no magic "inner binding" global { class Foo { @@ -43,6 +61,15 @@ assertThrowsInstanceOf(()=>eval(\`class Bar { } } assertEq(new Foo().test(), false); + assertEq(new class foo { + constructor() { }; + test() { + return new class bar { + constructor() { } + test() { return foo === bar } + }().test(); + } + }().test(), false); } // Inner bindings are shadowable @@ -52,8 +79,20 @@ assertThrowsInstanceOf(()=>eval(\`class Bar { test(Foo) { return Foo; } } assertEq(new Foo().test(4), 4); + assertEq(new class foo { + constructor() { }; + test(foo) { return foo } + }().test(4), 4); } +// Inner bindings in expressions should shadow even existing names. +class Foo { constructor() { } static method() { throw new Error("NO!"); } } +assertEq(new class Foo { + constructor() { }; + static method() { return 4; }; + test() { return Foo.method(); } + }().test(), 4); + // The outer binding is distinct from the inner one { let orig_X; @@ -68,6 +107,8 @@ assertThrowsInstanceOf(()=>eval(\`class Bar { assertEq(X, 13); new orig_X().f(); } + + `; eval(test); diff --git a/js/src/tests/ecma_6/Class/methodInstallation.js b/js/src/tests/ecma_6/Class/methodInstallation.js index 36311e5fb3..604f46c125 100644 --- a/js/src/tests/ecma_6/Class/methodInstallation.js +++ b/js/src/tests/ecma_6/Class/methodInstallation.js @@ -2,14 +2,14 @@ var test= ` -var methodCalled = false; -var getterCalled = false; -var setterCalled = false; -var constructorCalled = false; -var staticMethodCalled = false; -var staticGetterCalled = false; -var staticSetterCalled = false; -class a { +var methodCalled; +var getterCalled; +var setterCalled; +var constructorCalled; +var staticMethodCalled; +var staticGetterCalled; +var staticSetterCalled; +class testClass { constructor() { constructorCalled = true; } __proto__() { methodCalled = true } get getter() { getterCalled = true; } @@ -19,58 +19,80 @@ class a { static set staticSetter(x) { staticSetterCalled = true; } *[Symbol.iterator]() { yield "cow"; yield "pig"; } } -var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\"); -assertEq(aConstDesc.writable, true); -assertEq(aConstDesc.configurable, true); -assertEq(aConstDesc.enumerable, false); -aConstDesc.value(); -assertEq(constructorCalled, true); -// __proto__ is just an identifier for classes. No prototype changes are made. -assertEq(Object.getPrototypeOf(a.prototype), Object.prototype); -var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\"); -assertEq(aMethDesc.writable, true); -assertEq(aMethDesc.configurable, true); -assertEq(aMethDesc.enumerable, true); -aMethDesc.value(); -assertEq(methodCalled, true); +for (let a of [testClass, + class { + constructor() { constructorCalled = true; } + __proto__() { methodCalled = true } + get getter() { getterCalled = true; } + set setter(x) { setterCalled = true; } + static staticMethod() { staticMethodCalled = true; } + static get staticGetter() { staticGetterCalled = true; } + static set staticSetter(x) { staticSetterCalled = true; } + *[Symbol.iterator]() { yield "cow"; yield "pig"; } + }]) { -var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\"); -assertEq(aGetDesc.configurable, true); -assertEq(aGetDesc.enumerable, true); -aGetDesc.get(); -assertEq(getterCalled, true); + methodCalled = false; + getterCalled = false; + setterCalled = false; + constructorCalled = false; + staticMethodCalled = false; + staticGetterCalled = false; + staticSetterCalled = false; -var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\"); -assertEq(aSetDesc.configurable, true); -assertEq(aSetDesc.enumerable, true); -aSetDesc.set(); -assertEq(setterCalled, true); -assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\")); + var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\"); + assertEq(aConstDesc.writable, true); + assertEq(aConstDesc.configurable, true); + assertEq(aConstDesc.enumerable, false); + aConstDesc.value(); + assertEq(constructorCalled, true); -assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticMethod\"), undefined); -var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, \"staticMethod\"); -assertEq(aStaticMethDesc.configurable, true); -assertEq(aStaticMethDesc.enumerable, true); -assertEq(aStaticMethDesc.writable, true); -aStaticMethDesc.value(); -assertEq(staticMethodCalled, true); + // __proto__ is just an identifier for classes. No prototype changes are made. + assertEq(Object.getPrototypeOf(a.prototype), Object.prototype); + var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\"); + assertEq(aMethDesc.writable, true); + assertEq(aMethDesc.configurable, true); + assertEq(aMethDesc.enumerable, true); + aMethDesc.value(); + assertEq(methodCalled, true); -assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticGetter\"), undefined); -var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, \"staticGetter\"); -assertEq(aStaticGetDesc.configurable, true); -assertEq(aStaticGetDesc.enumerable, true); -aStaticGetDesc.get(); -assertEq(staticGetterCalled, true); + var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\"); + assertEq(aGetDesc.configurable, true); + assertEq(aGetDesc.enumerable, true); + aGetDesc.get(); + assertEq(getterCalled, true); -assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticSetter\"), undefined); -var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, \"staticSetter\"); -assertEq(aStaticSetDesc.configurable, true); -assertEq(aStaticSetDesc.enumerable, true); -aStaticSetDesc.set(); -assertEq(staticSetterCalled, true); + var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\"); + assertEq(aSetDesc.configurable, true); + assertEq(aSetDesc.enumerable, true); + aSetDesc.set(); + assertEq(setterCalled, true); + assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\")); -assertEq([...new a()].join(), "cow,pig"); + assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticMethod\"), undefined); + var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, \"staticMethod\"); + assertEq(aStaticMethDesc.configurable, true); + assertEq(aStaticMethDesc.enumerable, true); + assertEq(aStaticMethDesc.writable, true); + aStaticMethDesc.value(); + assertEq(staticMethodCalled, true); + + assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticGetter\"), undefined); + var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, \"staticGetter\"); + assertEq(aStaticGetDesc.configurable, true); + assertEq(aStaticGetDesc.enumerable, true); + aStaticGetDesc.get(); + assertEq(staticGetterCalled, true); + + assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticSetter\"), undefined); + var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, \"staticSetter\"); + assertEq(aStaticSetDesc.configurable, true); + assertEq(aStaticSetDesc.enumerable, true); + aStaticSetDesc.set(); + assertEq(staticSetterCalled, true); + + assertEq([...new a()].join(), "cow,pig"); +} `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/Class/methodOverwrites.js b/js/src/tests/ecma_6/Class/methodOverwrites.js index 9af84a212c..87cd371961 100644 --- a/js/src/tests/ecma_6/Class/methodOverwrites.js +++ b/js/src/tests/ecma_6/Class/methodOverwrites.js @@ -1,41 +1,83 @@ // Ensure that we can overwrite methods when more tha one is present. var test = ` -var result = 0; -// Regardless of order, the constructor is overridden by any CPN, because it's -// processed seperately. -class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } } -var aInst = new a(); -assertEq(result, 2); -aInst.constructor(); -assertEq(result, 3); -class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; } -var bInst = new b(); -assertEq(result, 5); -bInst.constructor(); -assertEq(result, 6); +{ + var result = 0; + // Regardless of order, the constructor is overridden by any CPN, because it's + // processed seperately. + class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } } + var aInst = new a(); + assertEq(result, 2); + aInst.constructor(); + assertEq(result, 3); -class c { constructor() { } method() { result += 1 } get method() { result += 2 } } -new c().method; -assertEq(result, 8); + class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; } + var bInst = new b(); + assertEq(result, 5); + bInst.constructor(); + assertEq(result, 6); -class d { constructor() { } get method() { result += 1 } method() { result += 2 } } -new d().method(); -assertEq(result, 10); + class c { constructor() { } method() { result += 1 } get method() { result += 2 } } + new c().method; + assertEq(result, 8); -// getters and setter should not overwrite each other, but merge cleanly. -class e { constructor() { } get method() { result += 1 } set method(x) { } } -new e().method; -assertEq(result, 11); + class d { constructor() { } get method() { result += 1 } method() { result += 2 } } + new d().method(); + assertEq(result, 10); -class f { constructor() { } - set method(x) { throw "NO"; } - method() { throw "NO" } - get method() { return new Function("result += 1"); } - } -new f().method(); -assertEq(result, 12); + // getters and setter should not overwrite each other, but merge cleanly. + class e { constructor() { } get method() { result += 1 } set method(x) { } } + new e().method; + assertEq(result, 11); + + class f { constructor() { } + set method(x) { throw "NO"; } + method() { throw "NO" } + get method() { return new Function("result += 1"); } + } + new f().method(); + assertEq(result, 12); +} + +// Try again with expressions. +{ + var result = 0; + // Regardless of order, the constructor is overridden by any CPN, because it's + // processed seperately. + let a = class { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } }; + var aInst = new a(); + assertEq(result, 2); + aInst.constructor(); + assertEq(result, 3); + + let b = class { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; }; + var bInst = new b(); + assertEq(result, 5); + bInst.constructor(); + assertEq(result, 6); + + let c = class { constructor() { } method() { result += 1 } get method() { result += 2 } }; + new c().method; + assertEq(result, 8); + + let d = class { constructor() { } get method() { result += 1 } method() { result += 2 } }; + new d().method(); + assertEq(result, 10); + + // getters and setter should not overwrite each other, but merge cleanly. + let e = class { constructor() { } get method() { result += 1 } set method(x) { } }; + new e().method; + assertEq(result, 11); + + let f = class { constructor() { } + set method(x) { throw "NO"; } + method() { throw "NO" } + get method() { return new Function("result += 1"); } + }; + new f().method(); + assertEq(result, 12); +} `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/Class/outerBinding.js b/js/src/tests/ecma_6/Class/outerBinding.js index 66595bb5c7..a5475db827 100644 --- a/js/src/tests/ecma_6/Class/outerBinding.js +++ b/js/src/tests/ecma_6/Class/outerBinding.js @@ -1,4 +1,4 @@ -// A class creates a mutable lexical outer binding. +// A class statement creates a mutable lexical outer binding. var test = ` class Foo { constructor() { } } diff --git a/js/src/tests/ecma_6/Class/strictExecution.js b/js/src/tests/ecma_6/Class/strictExecution.js index 6b072218d0..9608d47e1f 100644 --- a/js/src/tests/ecma_6/Class/strictExecution.js +++ b/js/src/tests/ecma_6/Class/strictExecution.js @@ -4,21 +4,36 @@ var test = ` class a { constructor() { Object.preventExtensions({}).prop = 0; } } assertThrowsInstanceOf(() => new a(), TypeError); +var aExpr = class { constructor() { Object.preventExtensions().prop = 0; } }; +assertThrowsInstanceOf(() => new aExpr(), TypeError); -function shouldThrow() { +function shouldThrowCPN() { class b { [Object.preventExtensions({}).prop = 4]() { } constructor() { } } } -assertThrowsInstanceOf(shouldThrow, TypeError); +function shouldThrowCPNExpr() { + var b = class { + [Object.preventExtensions({}).prop = 4]() { } + constructor() { } + }; +} +assertThrowsInstanceOf(shouldThrowCPN, TypeError); +assertThrowsInstanceOf(shouldThrowCPNExpr, TypeError); -function shouldThrow2() { +function shouldThrowHeritage() { class b extends (Object.preventExtensions({}).prop = 4) { constructor() { } } } -assertThrowsInstanceOf(shouldThrow2, TypeError); +function shouldThrowHeritageExpr() { + var b = class extends (Object.preventExtensions({}).prop = 4) { + constructor() { } + }; +} +assertThrowsInstanceOf(shouldThrowHeritage, TypeError); +assertThrowsInstanceOf(shouldThrowHeritageExpr, TypeError); `; if (classesEnabled()) diff --git a/js/src/tests/js1_8_5/extensions/reflect-parse.js b/js/src/tests/js1_8_5/extensions/reflect-parse.js index 7bff1055be..bfc11fc973 100644 --- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -72,6 +72,10 @@ function classStmt(id, heritage, body) Pattern({ type: "ClassStatement", name: id, heritage: heritage, body: body}) +function classExpr(id, heritage, body) Pattern({ type: "ClassExpression", + name: id, + heritage: heritage, + body: body}) function classMethod(id, body, kind, static) Pattern({ type: "ClassMethod", name: id, body: body, @@ -1158,9 +1162,6 @@ function classesEnabled() { } function testClasses() { - // No unnamed class statements. - assertError("class { constructor() { } }", SyntaxError); - function simpleMethod(id, kind, generator, args=[], isStatic=false) { assertEq(generator && kind === "method", generator); let idN = ident(id); @@ -1175,76 +1176,98 @@ function testClasses() { funExpr(null, [], blockStmt([])), "method", isStatic); } - function setClassMethods(class_, methods) { - class_.template.body = methods; + + function assertClassExpr(str, methods, heritage=null, name=null) { + let template = classExpr(name, heritage, methods); + assertExpr("(" + str + ")", template); } - function setClassHeritage(class_, heritage) { - class_.template.heritage = heritage; + function assertClass(str, methods, heritage=null) { + let namelessStr = str.replace("NAME", ""); + let namedStr = str.replace("NAME", "Foo"); + assertClassExpr(namelessStr, methods, heritage); + assertClassExpr(namedStr, methods, heritage, ident("Foo")); + + let template = classStmt(ident("Foo"), heritage, methods); + assertStmt(namedStr, template); + } + function assertNamedClassError(str, error) { + assertError(str, error); + assertError("(" + str + ")", error); + } + function assertClassError(str, error) { + assertNamedClassError(str, error); + assertError("(" + str.replace("NAME", "") + ")", error); } let simpleConstructor = simpleMethod("constructor", "method", false); - let emptyFooClass = classStmt(ident("Foo"), null, [simpleConstructor]); /* Trivial classes */ - assertStmt("class Foo { constructor() { } }", emptyFooClass); + // Unnamed class statements are forbidden, but unnamed class expressions are + // just fine. + assertError("class { constructor() { } }", SyntaxError); + assertClass("class NAME { constructor() { } }", [simpleConstructor]); + + // A class name must actually be a name + assertNamedClassError("class x.y { constructor() {} }", SyntaxError); + assertNamedClassError("class [] { constructor() {} }", SyntaxError); + assertNamedClassError("class {x} { constructor() {} }", SyntaxError); + assertNamedClassError("class for { constructor() {} }", SyntaxError); // Allow methods and accessors - let stmt = classStmt(ident("Foo"), null, - [simpleConstructor, simpleMethod("method", "method", false)]); - assertStmt("class Foo { constructor() { } method() { } }", stmt); + assertClass("class NAME { constructor() { } method() { } }", + [simpleConstructor, simpleMethod("method", "method", false)]); - setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "get", false)]); - assertStmt("class Foo { constructor() { } get method() { } }", stmt); + assertClass("class NAME { constructor() { } get method() { } }", + [simpleConstructor, simpleMethod("method", "get", false)]); - setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "set", false, ["x"])]); - assertStmt("class Foo { constructor() { } set method(x) { } }", stmt); + assertClass("class NAME { constructor() { } set method(x) { } }", + [simpleConstructor, simpleMethod("method", "set", false, ["x"])]); /* Static */ - setClassMethods(stmt, [simpleConstructor, - simpleMethod("method", "method", false, [], true), - simpleMethod("methodGen", "method", true, [], true), - simpleMethod("getter", "get", false, [], true), - simpleMethod("setter", "set", false, ["x"], true)]); - assertStmt(`class Foo { - constructor() { }; - static method() { }; - static *methodGen() { }; - static get getter() { }; - static set setter(x) { } - }`, stmt); - + assertClass(`class NAME { + constructor() { }; + static method() { }; + static *methodGen() { }; + static get getter() { }; + static set setter(x) { } + }`, + [simpleConstructor, + simpleMethod("method", "method", false, [], true), + simpleMethod("methodGen", "method", true, [], true), + simpleMethod("getter", "get", false, [], true), + simpleMethod("setter", "set", false, ["x"], true)]); // It's not an error to have a method named static, static, or not. - setClassMethods(stmt, [simpleConstructor, simpleMethod("static", "method", false, [], false)]); - assertStmt("class Foo{ constructor() { } static() { } }", stmt); - setClassMethods(stmt, [simpleMethod("static", "method", false, [], true), simpleConstructor]); - assertStmt("class Foo{ static static() { }; constructor() { } }", stmt); - setClassMethods(stmt, [simpleMethod("static", "get", false, [], true), simpleConstructor]); - assertStmt("class Foo { static get static() { }; constructor() { } }", stmt); - setClassMethods(stmt, [simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]); - assertStmt("class Foo { constructor() { }; static set static(x) { } }", stmt); + assertClass("class NAME { constructor() { } static() { } }", + [simpleConstructor, simpleMethod("static", "method", false)]); + assertClass("class NAME { static static() { }; constructor() { } }", + [simpleMethod("static", "method", false, [], true), simpleConstructor]); + assertClass("class NAME { static get static() { }; constructor() { } }", + [simpleMethod("static", "get", false, [], true), simpleConstructor]); + assertClass("class NAME { constructor() { }; static set static(x) { } }", + [simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]); // You do, however, have to put static in the right spot - assertError("class Foo { constructor() { }; get static foo() { } }", SyntaxError); + assertClassError("class NAME { constructor() { }; get static foo() { } }", SyntaxError); // Spec disallows "prototype" as a static member in a class, since that // one's important to make the desugaring work - assertError("class Foo { constructor() { } static prototype() { } }", SyntaxError); - assertError("class Foo { constructor() { } static *prototype() { } }", SyntaxError); - assertError("class Foo { static get prototype() { }; constructor() { } }", SyntaxError); - assertError("class Foo { static set prototype(x) { }; constructor() { } }", SyntaxError); + assertClassError("class NAME { constructor() { } static prototype() { } }", SyntaxError); + assertClassError("class NAME { constructor() { } static *prototype() { } }", SyntaxError); + assertClassError("class NAME { static get prototype() { }; constructor() { } }", SyntaxError); + assertClassError("class NAME { static set prototype(x) { }; constructor() { } }", SyntaxError); // You are, however, allowed to have a CPN called prototype as a static - setClassMethods(stmt, [simpleConstructor, emptyCPNMethod("prototype", true)]); - assertStmt("class Foo { constructor() { }; static [\"prototype\"]() { } }", stmt); + assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }", + [simpleConstructor, emptyCPNMethod("prototype", true)]); /* Constructor */ // Currently, we do not allow default constructors - assertError("class Foo { }", TypeError); + assertClassError("class NAME { }", TypeError); // It is an error to have two methods named constructor, but not other // names, regardless if one is an accessor or a generator or static. - assertError("class Foo { constructor() { } constructor(a) { } }", SyntaxError); + assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError); let methods = [["method() { }", simpleMethod("method", "method", false)], ["*method() { }", simpleMethod("method", "method", true)], ["get method() { }", simpleMethod("method", "get", false)], @@ -1256,66 +1279,93 @@ function testClasses() { let i,j; for (i=0; i < methods.length; i++) { for (j=0; j < methods.length; j++) { - setClassMethods(stmt, - [simpleConstructor, - methods[i][1], - methods[j][1]]); - let str = "class Foo { constructor() { } " + + let str = "class NAME { constructor() { } " + methods[i][0] + " " + methods[j][0] + " }"; - assertStmt(str, stmt); + assertClass(str, [simpleConstructor, methods[i][1], methods[j][1]]); } } // It is, however, not an error to have a constructor, and a method with a // computed property name 'constructor' - setClassMethods(stmt, [simpleConstructor, emptyCPNMethod("constructor", false)]); - assertStmt("class Foo { constructor () { } [\"constructor\"] () { } }", stmt); + assertClass("class NAME { constructor () { } [\"constructor\"] () { } }", + [simpleConstructor, emptyCPNMethod("constructor", false)]); // It is an error to have a generator or accessor named constructor - assertError("class Foo { *constructor() { } }", SyntaxError); - assertError("class Foo { get constructor() { } }", SyntaxError); - assertError("class Foo { set constructor() { } }", SyntaxError); + assertClassError("class NAME { *constructor() { } }", SyntaxError); + assertClassError("class NAME { get constructor() { } }", SyntaxError); + assertClassError("class NAME { set constructor() { } }", SyntaxError); /* Semicolons */ // Allow Semicolons in Class Definitions - assertStmt("class Foo { constructor() { }; }", emptyFooClass); + assertClass("class NAME { constructor() { }; }", [simpleConstructor]); // Allow more than one semicolon, even in otherwise trivial classses - assertStmt("class Foo { ;;; constructor() { } }", emptyFooClass); + assertClass("class NAME { ;;; constructor() { } }", [simpleConstructor]); // Semicolons are optional, even if the methods share a line - setClassMethods(stmt, [simpleMethod("method", "method", false), simpleConstructor]); - assertStmt("class Foo { method() { } constructor() { } }", stmt); + assertClass("class NAME { method() { } constructor() { } }", + [simpleMethod("method", "method", false), simpleConstructor]); /* Generators */ // No yield as a class name inside a generator - assertError("function *foo() {\ - class yield {\ - constructor() { } \ - }\ - }", SyntaxError); + assertError(`function *foo() { + class yield { + constructor() { } + } + }`, SyntaxError); + assertError(`function *foo() { + (class yield { + constructor() { } + }) + }`, SyntaxError); // Methods may be generators, but not accessors - assertError("class Foo { constructor() { } *get foo() { } }", SyntaxError); - assertError("class Foo { constructor() { } *set foo() { } }", SyntaxError); + assertClassError("class NAME { constructor() { } *get foo() { } }", SyntaxError); + assertClassError("class NAME { constructor() { } *set foo() { } }", SyntaxError); - setClassMethods(stmt, [simpleMethod("method", "method", true), simpleConstructor]); - assertStmt("class Foo { *method() { } constructor() { } }", stmt); + assertClass("class NAME { *method() { } constructor() { } }", + [simpleMethod("method", "method", true), simpleConstructor]); /* Strictness */ // yield is a strict-mode keyword, and class definitions are always strict. - assertError("class Foo { constructor() { var yield; } }", SyntaxError); + assertClassError("class NAME { constructor() { var yield; } }", SyntaxError); + // Beware of the strictness of computed property names. Here use bareword // deletion (a deprecated action) to check. - assertError("class Foo { constructor() { } [delete bar]() { }}", SyntaxError); + assertClassError("class NAME { constructor() { } [delete bar]() { }}", SyntaxError); /* Bindings */ - // Classes should bind lexically, so they should collide with other in-block - // lexical bindings + // Class statements bind lexically, so they should collide with other + // in-block lexical bindings, but class expressions don't. assertError("{ let Foo; class Foo { constructor() { } } }", TypeError); + assertStmt("{ let Foo; (class Foo { constructor() { } }) }", + blockStmt([letDecl([{id: ident("Foo"), init: null}]), + exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))])); assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError); + assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }", + blockStmt([constDecl([{id: ident("Foo"), init: lit(0)}]), + exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))])); assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError); + assertStmt(`{ + (class Foo { + constructor() { } + }, + class Foo { + constructor() { } + }); + }`, + blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [simpleConstructor]), + classExpr(ident("Foo"), null, [simpleConstructor])]))])); + assertStmt(`{ + var x = class Foo { constructor() { } }; + class Foo { constructor() { } } + }`, + blockStmt([varDecl([{ id: ident("x"), + init: classExpr(ident("Foo"), null, [simpleConstructor]) + }]), + classStmt(ident("Foo"), null, [simpleConstructor])])); + // Can't make a lexical binding inside a block. assertError("if (1) class Foo { constructor() { } }", SyntaxError); @@ -1323,65 +1373,64 @@ function testClasses() { /* Heritage Expressions */ // It's illegal to have things that look like "multiple inheritance": // non-parenthesized comma expressions. - assertError("class Foo extends null, undefined { constructor() { } }", SyntaxError); + assertClassError("class NAME extends null, undefined { constructor() { } }", SyntaxError); // Again check for strict-mode in heritage expressions - assertError("class Foo extends (delete x) { constructor() { } }", SyntaxError); + assertClassError("class NAME extends (delete x) { constructor() { } }", SyntaxError); // You must specify an inheritance if you say "extends" - assertError("class Foo extends { constructor() { } }", SyntaxError); + assertClassError("class NAME extends { constructor() { } }", SyntaxError); // "extends" is still a valid name for a method - setClassMethods(stmt, [simpleConstructor, simpleMethod("extends", "method", false)]); - assertStmt("class Foo { constructor() { }; extends() { } }", stmt); + assertClass("class NAME { constructor() { }; extends() { } }", + [simpleConstructor, simpleMethod("extends", "method", false)]); // Immediate expression - setClassMethods(stmt, [simpleConstructor]); - setClassHeritage(stmt, lit(null)); - assertStmt("class Foo extends null { constructor() { } }", stmt); + assertClass("class NAME extends null { constructor() { } }", + [simpleConstructor], lit(null)); // Sequence expresson - setClassHeritage(stmt, seqExpr([ident("undefined"), ident("undefined")])); - assertStmt("class Foo extends (undefined, undefined) { constructor() { } }", stmt); + assertClass("class NAME extends (undefined, undefined) { constructor() { } }", + [simpleConstructor], seqExpr([ident("undefined"), ident("undefined")])); // Function expression let emptyFunction = funExpr(null, [], blockStmt([])); - setClassHeritage(stmt, emptyFunction); - assertStmt("class Foo extends function(){ } { constructor() { } }", stmt); + assertClass("class NAME extends function(){ } { constructor() { } }", + [simpleConstructor], emptyFunction); // New expression - setClassHeritage(stmt, newExpr(emptyFunction, [])); - assertStmt("class Foo extends new function(){ }() { constructor() { } }", stmt); + assertClass("class NAME extends new function(){ }() { constructor() { } }", + [simpleConstructor], newExpr(emptyFunction, [])); // Call expression - setClassHeritage(stmt, callExpr(emptyFunction, [])); - assertStmt("class Foo extends function(){ }() { constructor() { } }", stmt); + assertClass("class NAME extends function(){ }() { constructor() { } }", + [simpleConstructor], callExpr(emptyFunction, [])); // Dot expression - setClassHeritage(stmt, dotExpr(objExpr([]), ident("foo"))); - assertStmt("class Foo extends {}.foo { constructor() { } }", stmt); + assertClass("class NAME extends {}.foo { constructor() { } }", + [simpleConstructor], dotExpr(objExpr([]), ident("foo"))); // Member expression - setClassHeritage(stmt, memExpr(objExpr([]), ident("foo"))); - assertStmt("class Foo extends {}[foo] { constructor() { } }", stmt); + assertClass("class NAME extends {}[foo] { constructor() { } }", + [simpleConstructor], memExpr(objExpr([]), ident("foo"))); /* EOF */ // Clipped classes should throw a syntax error - assertError("class Foo {", SyntaxError); - assertError("class Foo {;", SyntaxError); - assertError("class Foo { constructor", SyntaxError); - assertError("class Foo { constructor(", SyntaxError); - assertError("class Foo { constructor()", SyntaxError); - assertError("class Foo { constructor()", SyntaxError); - assertError("class Foo { constructor() {", SyntaxError); - assertError("class Foo { constructor() { }", SyntaxError); - assertError("class Foo { static", SyntaxError); - assertError("class Foo { static y", SyntaxError); - assertError("class Foo { static *", SyntaxError); - assertError("class Foo { static *y", SyntaxError); - assertError("class Foo { static get", SyntaxError); - assertError("class Foo { static get y", SyntaxError); - assertError("class Foo extends", SyntaxError); + assertClassError("class NAME {", SyntaxError); + assertClassError("class NAME {;", SyntaxError); + assertClassError("class NAME { constructor", SyntaxError); + assertClassError("class NAME { constructor(", SyntaxError); + assertClassError("class NAME { constructor()", SyntaxError); + assertClassError("class NAME { constructor()", SyntaxError); + assertClassError("class NAME { constructor() {", SyntaxError); + assertClassError("class NAME { constructor() { }", SyntaxError); + assertClassError("class NAME { static", SyntaxError); + assertClassError("class NAME { static y", SyntaxError); + assertClassError("class NAME { static *", SyntaxError); + assertClassError("class NAME { static *y", SyntaxError); + assertClassError("class NAME { static get", SyntaxError); + assertClassError("class NAME { static get y", SyntaxError); + assertClassError("class NAME extends", SyntaxError); } if (classesEnabled()) diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index a38f008bc8..f63f14c3d6 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -169,7 +169,7 @@ ArgumentsObject::create(JSContext* cx, HandleScript script, HandleFunction calle if (!group) return nullptr; - JSObject* metadata = nullptr; + JSObject *metadata = nullptr; if (!NewObjectMetadata(cx, &metadata)) return nullptr; diff --git a/js/src/vm/ArrayObject-inl.h b/js/src/vm/ArrayObject-inl.h index c42cf75f5b..ca695945b2 100644 --- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -102,8 +102,8 @@ ArrayObject::createArray(ExclusiveContext* cx, gc::InitialHeap heap, return finishCreateArray(obj, shape); } -/* static */ inline ArrayObject* -ArrayObject::createCopyOnWriteArray(ExclusiveContext* cx, gc::InitialHeap heap, +/* static */ inline ArrayObject * +ArrayObject::createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap, HandleShape shape, HandleArrayObject sharedElementsOwner) { @@ -116,7 +116,7 @@ ArrayObject::createCopyOnWriteArray(ExclusiveContext* cx, gc::InitialHeap heap, gc::AllocKind kind = gc::AllocKind::OBJECT0_BACKGROUND; RootedObjectGroup group(cx, sharedElementsOwner->group()); - ArrayObject* obj = createArrayInternal(cx, kind, heap, shape, group); + ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group); if (!obj) return nullptr; diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index f1ece29840..27a0291248 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -181,11 +181,11 @@ class Debugger : private mozilla::LinkedListElement friend class DebuggerMemory; friend class SavedStacks; friend class mozilla::LinkedListElement; - friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj); + friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj); friend bool (::JS::dbg::IsDebugger)(JS::Value val); - friend bool SavedStacksMetadataCallback(JSContext* cx, JSObject** pmetadata); - friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise); - friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise); + friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); + friend void JS::dbg::onNewPromise(JSContext *cx, HandleObject promise); + friend void JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise); public: enum Hook { diff --git a/js/src/vm/Runtime-inl.h b/js/src/vm/Runtime-inl.h index 0f45e6807b..56c942d969 100644 --- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -59,26 +59,15 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, gc::Initi if (cx->runtime()->gc.upcomingZealousGC()) return nullptr; - NativeObject *obj = gc::AllocateObjectForCacheHit(cx, entry->kind, heap, group->clasp()); - if (obj) { - copyCachedToObject(obj, templateObj, entry->kind); - probes::CreateObject(cx, obj); - gc::TraceCreateObject(obj); - return obj; - } + NativeObject *obj = static_cast(Allocate(cx, entry->kind, 0, + heap, group->clasp())); + if (!obj) + return nullptr; - // Trigger an identical allocation to the one that notified us of OOM so - // that we trigger the right kind of GC automatically; note that even - // though we are passing CanGC to AllocateObjectForCacheHit it will never - // allow GC during the allocation itself. The reason is that this would - // clobber our cache and make us unable to initialize from it. Instead we - // do an independent non-allocation GC, then return nullptr so that we'll - // take the slow allocation path. The callee is responsible for ensuring - // that the index it uses to fill the cache is still correct after this GC. - mozilla::DebugOnly obj2 = - gc::AllocateObjectForCacheHit(cx, entry->kind, heap, group->clasp()); - MOZ_ASSERT(!obj2); - return nullptr; + copyCachedToObject(obj, templateObj, entry->kind); + probes::CreateObject(cx, obj); + gc::TraceCreateObject(obj); + return obj; } } /* namespace js */ diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index a4dacd0179..de16cf9185 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1149,9 +1149,9 @@ SavedStacks::chooseSamplingProbability(JSContext* cx) } bool -SavedStacksMetadataCallback(JSContext* cx, JSObject** pmetadata) +SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata) { - SavedStacks& stacks = cx->compartment()->savedStacks(); + SavedStacks &stacks = cx->compartment()->savedStacks(); if (stacks.allocationSkipCount > 0) { stacks.allocationSkipCount--; return true; @@ -1194,7 +1194,7 @@ SavedStacksMetadataCallback(JSContext* cx, JSObject** pmetadata) return Debugger::onLogAllocationSite(cx, frame, PRMJ_Now()); } -JS_FRIEND_API(JSPrincipals*) +JS_FRIEND_API(JSPrincipals *) GetSavedFramePrincipals(HandleObject savedFrame) { MOZ_ASSERT(savedFrame); diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index bd5094af18..6f981a9d0a 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -125,7 +125,7 @@ struct SavedFrame::HashPolicy }; class SavedStacks { - friend bool SavedStacksMetadataCallback(JSContext* cx, JSObject** pmetadata); + friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); public: SavedStacks() @@ -244,10 +244,10 @@ class SavedStacks { PCLocationMap pcLocationMap; void sweepPCLocationMap(); - bool getLocation(JSContext* cx, const FrameIter& iter, MutableHandleLocationValue locationp); + bool getLocation(JSContext *cx, const FrameIter &iter, MutableHandleLocationValue locationp); }; -bool SavedStacksMetadataCallback(JSContext* cx, JSObject** pmetadata); +bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); } /* namespace js */ diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 158ef51851..66de054c07 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1889,15 +1889,15 @@ js::IsDebugScopeSlow(ProxyObject *proxy) /*****************************************************************************/ /* static */ MOZ_ALWAYS_INLINE void -DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime* rt, ObjectWeakMap* map, - const PreBarrieredObject& key) +DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, + const PreBarrieredObject &key) { if (key && IsInsideNursery(key)) rt->gc.storeBuffer.putGeneric(UnbarrieredRef(map, key.get())); } /* static */ MOZ_ALWAYS_INLINE void -DebugScopes::liveScopesPostWriteBarrier(JSRuntime* rt, LiveScopeMap* map, ScopeObject* key) +DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key) { // As above. Otherwise, barriers could fire during GC when moving the // value. diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 006810e53e..44467b6ec8 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -921,8 +921,8 @@ class DebugScopes /* The map from (non-debug) scopes to debug scopes. */ typedef WeakMap ObjectWeakMap; ObjectWeakMap proxiedScopes; - static MOZ_ALWAYS_INLINE void proxiedScopesPostWriteBarrier(JSRuntime* rt, ObjectWeakMap* map, - const PreBarrieredObject& key); + static MOZ_ALWAYS_INLINE void proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, + const PreBarrieredObject &key); /* * The map from live frames which have optimized-away scopes to the diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index fe43871e8e..c3abd13585 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1159,7 +1159,7 @@ JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata) } /* static */ Shape * -Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape* last) +Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last) { if (last->getObjectMetadata() == metadata) return last; @@ -1245,7 +1245,7 @@ StackBaseShape::hash(const Lookup& lookup) } /* static */ inline bool -StackBaseShape::match(UnownedBaseShape* key, const Lookup& lookup) +StackBaseShape::match(UnownedBaseShape *key, const Lookup& lookup) { return key->flags == lookup.flags && key->clasp_ == lookup.clasp @@ -1371,7 +1371,7 @@ JSCompartment::fixupBaseShapeTable() for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { UnownedBaseShape *base = e.front().unbarrieredGet(); if (base->fixupBaseShapeTableEntry()) { - ReadBarriered b(base); + ReadBarriered b(base); e.rekeyFront(base, b); } } diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 827f5d8b45..3a8d694a80 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -470,11 +470,11 @@ class BaseShape : public gc::TenuredCell return *this; } - const Class* clasp() const { return clasp_; } + const Class *clasp() const { return clasp_; } bool isOwned() const { return !!(flags & OWNED_SHAPE); } - inline void adoptUnowned(UnownedBaseShape* other); + inline void adoptUnowned(UnownedBaseShape *other); void setOwned(UnownedBaseShape* unowned) { flags |= OWNED_SHAPE; @@ -485,13 +485,13 @@ class BaseShape : public gc::TenuredCell uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; } bool hasTable() const { MOZ_ASSERT_IF(table_, isOwned()); return table_ != nullptr; } - ShapeTable& table() const { MOZ_ASSERT(table_ && isOwned()); return *table_; } - void setTable(ShapeTable* table) { MOZ_ASSERT(isOwned()); table_ = table; } + ShapeTable &table() const { MOZ_ASSERT(table_ && isOwned()); return *table_; } + void setTable(ShapeTable *table) { MOZ_ASSERT(isOwned()); table_ = table; } uint32_t slotSpan() const { MOZ_ASSERT(isOwned()); return slotSpan_; } void setSlotSpan(uint32_t slotSpan) { MOZ_ASSERT(isOwned()); slotSpan_ = slotSpan; } - JSCompartment* compartment() const { return compartment_; } + JSCompartment *compartment() const { return compartment_; } /* * Lookup base shapes from the compartment's baseShapes table, adding if @@ -601,7 +601,7 @@ struct StackBaseShape : public DefaultHasher matchMetadata(base.metadata) {} - MOZ_IMPLICIT Lookup(UnownedBaseShape* base) + MOZ_IMPLICIT Lookup(UnownedBaseShape *base) : flags(base->getObjectFlags()), clasp(base->clasp()), hashMetadata(base->getObjectMetadata()), @@ -621,14 +621,14 @@ struct StackBaseShape : public DefaultHasher }; static inline HashNumber hash(const Lookup& lookup); - static inline bool match(UnownedBaseShape* key, const Lookup& lookup); + static inline bool match(UnownedBaseShape *key, const Lookup& lookup); // For RootedGeneric - void trace(JSTracer* trc); + void trace(JSTracer *trc); }; inline -BaseShape::BaseShape(const StackBaseShape& base) +BaseShape::BaseShape(const StackBaseShape &base) { mozilla::PodZero(this); this->clasp_ = base.clasp; diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 94c0d5e395..00c56af8f4 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -72,6 +72,9 @@ UnboxedLayout::makeConstructorCode(JSContext *cx, HandleObjectGroup group) { using namespace jit; + if (!cx->compartment()->ensureJitCompartmentExists(cx)) + return false; + UnboxedLayout &layout = group->unboxedLayout(); MOZ_ASSERT(!layout.constructorCode());