/* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2003-2019 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * Copyright (C) 2007 Eric Seidel * Copyright (C) 2012 Igalia, S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "Nodes.h" #include "NodeConstructors.h" #include "BuiltinNames.h" #include "BytecodeGenerator.h" #include "BytecodeGeneratorBaseInlines.h" #include "JSArrayIterator.h" #include "JSAsyncGenerator.h" #include "JSCInlines.h" #include "JSGenerator.h" #include "JSImmutableButterfly.h" #include "JSMapIterator.h" #include "JSSetIterator.h" #include "JSStringIterator.h" #include "LabelScope.h" #include "LinkTimeConstant.h" #include "ModuleScopeData.h" #include "StackAlignment.h" #include "UnlinkedMetadataTableInlines.h" #include "YarrFlags.h" #include #include #ifdef DARLING_NONUNIFIED_BUILD #include "BytecodeStructs.h" #endif namespace JSC { /* Details of the emitBytecode function. Return value: The register holding the production's value. dst: An optional parameter specifying the most efficient destination at which to store the production's value. If dst is null, you may return whatever VirtualRegister you want. Otherwise you have to return dst. The dst argument provides for a crude form of copy propagation. For example, x = 1 becomes load r[x], 1 instead of load r0, 1 mov r[x], r0 because the assignment node, "x =", passes r[x] as dst to the number node, "1". */ void ExpressionNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label& trueTarget, Label& falseTarget, FallThroughMode fallThroughMode) { RegisterID* result = generator.emitNode(this); if (fallThroughMode == FallThroughMeansTrue) generator.emitJumpIfFalse(result, falseTarget); else generator.emitJumpIfTrue(result, trueTarget); } // ------------------------------ ThrowableExpressionData -------------------------------- RegisterID* ThrowableExpressionData::emitThrowReferenceError(BytecodeGenerator& generator, const String& message, RegisterID* dst) { generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitThrowReferenceError(message); if (dst) return dst; return generator.newTemporary(); } // ------------------------------ ConstantNode ---------------------------------- void ConstantNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label& trueTarget, Label& falseTarget, FallThroughMode fallThroughMode) { TriState value = TriState::Indeterminate; JSValue constant = jsValue(generator); if (LIKELY(constant)) value = constant.pureToBoolean(); if (UNLIKELY(needsDebugHook())) { if (value != TriState::Indeterminate) generator.emitDebugHook(this); } if (value == TriState::Indeterminate) ExpressionNode::emitBytecodeInConditionContext(generator, trueTarget, falseTarget, fallThroughMode); else if (value == TriState::True && fallThroughMode == FallThroughMeansFalse) generator.emitJump(trueTarget); else if (value == TriState::False && fallThroughMode == FallThroughMeansTrue) generator.emitJump(falseTarget); // All other cases are unconditional fall-throughs, like "if (true)". } RegisterID* ConstantNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return nullptr; JSValue constant = jsValue(generator); if (UNLIKELY(!constant)) { // This can happen if we try to parse a string or BigInt so enormous that we OOM. return generator.emitThrowExpressionTooDeepException(); } return generator.emitLoad(dst, constant); } JSValue StringNode::jsValue(BytecodeGenerator& generator) const { return generator.addStringConstant(m_value); } JSValue BigIntNode::jsValue(BytecodeGenerator& generator) const { return generator.addBigIntConstant(m_value, m_radix, m_sign); } // ------------------------------ NumberNode ---------------------------------- RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return nullptr; return generator.emitLoad(dst, jsValue(generator), isIntegerNode() ? SourceCodeRepresentation::Integer : SourceCodeRepresentation::Double); } // ------------------------------ RegExpNode ----------------------------------- RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return nullptr; auto flags = Yarr::parseFlags(m_flags.string()); ASSERT(flags.hasValue()); RegExp* regExp = RegExp::create(generator.vm(), m_pattern.string(), flags.value()); if (regExp->isValid()) return generator.emitNewRegExp(generator.finalDestination(dst), regExp); const char* messageCharacters = regExp->errorMessage(); const Identifier& message = generator.parserArena().identifierArena().makeIdentifier(generator.vm(), bitwise_cast(messageCharacters), strlen(messageCharacters)); generator.emitThrowStaticError(ErrorTypeWithExtension::SyntaxError, message); return generator.emitLoad(generator.finalDestination(dst), jsUndefined()); } // ------------------------------ ThisNode ------------------------------------- RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { generator.ensureThis(); if (dst == generator.ignoredResult()) return nullptr; RegisterID* result = generator.move(dst, generator.thisRegister()); static const unsigned thisLength = strlen("this"); generator.emitProfileType(generator.thisRegister(), position(), position() + thisLength); return result; } // ------------------------------ SuperNode ------------------------------------- static RegisterID* emitHomeObjectForCallee(BytecodeGenerator& generator) { if ((generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) && generator.parseMode() != SourceParseMode::ClassFieldInitializerMode) { RegisterID* derivedConstructor = generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); return generator.emitGetById(generator.newTemporary(), derivedConstructor, generator.propertyNames().builtinNames().homeObjectPrivateName()); } RegisterID callee; callee.setIndex(CallFrameSlot::callee); return generator.emitGetById(generator.newTemporary(), &callee, generator.propertyNames().builtinNames().homeObjectPrivateName()); } static RegisterID* emitSuperBaseForCallee(BytecodeGenerator& generator) { RefPtr homeObject = emitHomeObjectForCallee(generator); return generator.emitGetPrototypeOf(generator.newTemporary(), homeObject.get()); } static RegisterID* emitGetSuperFunctionForConstruct(BytecodeGenerator& generator) { if (generator.isDerivedConstructorContext()) return generator.emitGetPrototypeOf(generator.newTemporary(), generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment()); RegisterID callee; callee.setIndex(CallFrameSlot::callee); return generator.emitGetPrototypeOf(generator.newTemporary(), &callee); } RegisterID* SuperNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RegisterID* result = emitSuperBaseForCallee(generator); return generator.move(generator.finalDestination(dst), result); } // ------------------------------ ImportNode ------------------------------------- RegisterID* ImportNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr importModule = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::importModule); CallArguments arguments(generator, nullptr, 1); generator.emitLoad(arguments.thisRegister(), jsUndefined()); generator.emitNode(arguments.argumentRegister(0), m_expr); return generator.emitCall(generator.finalDestination(dst, importModule.get()), importModule.get(), NoExpectedFunction, arguments, divot(), divotStart(), divotEnd(), DebuggableCall::No); } // ------------------------------ NewTargetNode ---------------------------------- RegisterID* NewTargetNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return nullptr; return generator.move(dst, generator.newTarget()); } // ------------------------------ ImportMetaNode --------------------------------- RegisterID* ImportMetaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { return generator.emitNode(dst, m_expr); } // ------------------------------ ResolveNode ---------------------------------- bool ResolveNode::isPure(BytecodeGenerator& generator) const { return generator.variable(m_ident).offset().isStack(); } RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { Variable var = generator.variable(m_ident); if (RegisterID* local = var.local()) { generator.emitTDZCheckIfNecessary(var, local, nullptr); if (dst == generator.ignoredResult()) return nullptr; generator.emitProfileType(local, var, m_position, m_position + m_ident.length()); return generator.move(dst, local); } JSTextPosition divot = m_start + m_ident.length(); generator.emitExpressionInfo(divot, m_start, divot); RefPtr scope = generator.emitResolveScope(dst, var); RegisterID* finalDest = generator.finalDestination(dst); RefPtr uncheckedResult = generator.newTemporary(); generator.emitGetFromScope(uncheckedResult.get(), scope.get(), var, ThrowIfNotFound); generator.emitTDZCheckIfNecessary(var, uncheckedResult.get(), nullptr); generator.move(finalDest, uncheckedResult.get()); generator.emitProfileType(finalDest, var, m_position, m_position + m_ident.length()); return finalDest; } // ------------------------------ TemplateStringNode ----------------------------------- RegisterID* TemplateStringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return nullptr; ASSERT(cooked()); return generator.emitLoad(dst, JSValue(generator.addStringConstant(*cooked()))); } // ------------------------------ TemplateLiteralNode ----------------------------------- RegisterID* TemplateLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (!m_templateExpressions) { TemplateStringNode* templateString = m_templateStrings->value(); ASSERT_WITH_MESSAGE(!m_templateStrings->next(), "Only one template element exists because there's no expression in a given template literal."); return generator.emitNode(dst, templateString); } Vector, 16> temporaryRegisters; TemplateStringListNode* templateString = m_templateStrings; TemplateExpressionListNode* templateExpression = m_templateExpressions; for (; templateExpression; templateExpression = templateExpression->next(), templateString = templateString->next()) { // Evaluate TemplateString. ASSERT(templateString->value()->cooked()); if (!templateString->value()->cooked()->isEmpty()) { temporaryRegisters.append(generator.newTemporary()); generator.emitNode(temporaryRegisters.last().get(), templateString->value()); } // Evaluate Expression. temporaryRegisters.append(generator.newTemporary()); generator.emitNode(temporaryRegisters.last().get(), templateExpression->value()); generator.emitToString(temporaryRegisters.last().get(), temporaryRegisters.last().get()); } // Evaluate tail TemplateString. ASSERT(templateString->value()->cooked()); if (!templateString->value()->cooked()->isEmpty()) { temporaryRegisters.append(generator.newTemporary()); generator.emitNode(temporaryRegisters.last().get(), templateString->value()); } if (temporaryRegisters.size() == 1) return generator.emitToString(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get()); return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size()); } // ------------------------------ TaggedTemplateNode ----------------------------------- RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { ExpectedFunction expectedFunction = NoExpectedFunction; RefPtr tag = nullptr; RefPtr base = nullptr; if (!m_tag->isLocation()) { tag = generator.newTemporary(); tag = generator.emitNode(tag.get(), m_tag); } else if (m_tag->isResolveNode()) { ResolveNode* resolve = static_cast(m_tag); const Identifier& identifier = resolve->identifier(); expectedFunction = generator.expectedFunctionForIdentifier(identifier); Variable var = generator.variable(identifier); if (RegisterID* local = var.local()) { generator.emitTDZCheckIfNecessary(var, local, nullptr); tag = generator.move(generator.newTemporary(), local); } else { tag = generator.newTemporary(); base = generator.newTemporary(); JSTextPosition newDivot = divotStart() + identifier.length(); generator.emitExpressionInfo(newDivot, divotStart(), newDivot); generator.move(base.get(), generator.emitResolveScope(base.get(), var)); generator.emitGetFromScope(tag.get(), base.get(), var, ThrowIfNotFound); generator.emitTDZCheckIfNecessary(var, tag.get(), nullptr); } } else if (m_tag->isBracketAccessorNode()) { BracketAccessorNode* bracket = static_cast(m_tag); base = generator.newTemporary(); base = generator.emitNode(base.get(), bracket->base()); RefPtr property = generator.emitNodeForProperty(bracket->subscript()); if (bracket->base()->isSuperNode()) { RefPtr thisValue = generator.ensureThis(); tag = generator.emitGetByVal(generator.newTemporary(), base.get(), thisValue.get(), property.get()); } else tag = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); } else { ASSERT(m_tag->isDotAccessorNode()); DotAccessorNode* dot = static_cast(m_tag); base = generator.newTemporary(); base = generator.emitNode(base.get(), dot->base()); if (dot->base()->isSuperNode()) { RefPtr thisValue = generator.ensureThis(); tag = generator.emitGetById(generator.newTemporary(), base.get(), thisValue.get(), dot->identifier()); } else tag = generator.emitGetById(generator.newTemporary(), base.get(), dot->identifier()); } RefPtr templateObject = generator.emitGetTemplateObject(nullptr, this); unsigned expressionsCount = 0; for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next()) ++expressionsCount; CallArguments callArguments(generator, nullptr, 1 + expressionsCount); if (base) generator.move(callArguments.thisRegister(), base.get()); else generator.emitLoad(callArguments.thisRegister(), jsUndefined()); unsigned argumentIndex = 0; generator.move(callArguments.argumentRegister(argumentIndex++), templateObject.get()); for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next()) generator.emitNode(callArguments.argumentRegister(argumentIndex++), templateExpression->value()); return generator.emitCallInTailPosition(generator.finalDestination(dst, tag.get()), tag.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); } // ------------------------------ ArrayNode ------------------------------------ RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { bool hadVariableExpression = false; unsigned length = 0; IndexingType recommendedIndexingType = ArrayWithUndecided; ElementNode* firstPutElement; for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) { if (firstPutElement->elision() || firstPutElement->value()->isSpreadExpression()) break; if (!firstPutElement->value()->isConstant()) hadVariableExpression = true; else { JSValue constant = static_cast(firstPutElement->value())->jsValue(generator); if (UNLIKELY(!constant)) hadVariableExpression = true; else recommendedIndexingType = leastUpperBoundOfIndexingTypeAndValue(recommendedIndexingType, constant); } ++length; } auto newArray = [&] (RegisterID* dst, ElementNode* elements, unsigned length, bool hadVariableExpression) { if (length && !hadVariableExpression) { recommendedIndexingType |= CopyOnWrite; ASSERT(generator.vm().heap.isDeferred()); // We run bytecode generator under a DeferGC. If we stopped doing that, we'd need to put a DeferGC here as we filled in these slots. auto* array = JSImmutableButterfly::create(generator.vm(), recommendedIndexingType, length); unsigned index = 0; for (ElementNode* element = elements; index < length; element = element->next()) { ASSERT(element->value()->isConstant()); JSValue constant = static_cast(element->value())->jsValue(generator); ASSERT(constant); array->setIndex(generator.vm(), index++, constant); } return generator.emitNewArrayBuffer(dst, array, recommendedIndexingType); } return generator.emitNewArray(dst, elements, length, recommendedIndexingType); }; if (!firstPutElement && !m_elision) return newArray(generator.finalDestination(dst), m_element, length, hadVariableExpression); if (firstPutElement && firstPutElement->value()->isSpreadExpression()) { bool hasElision = m_elision; if (!hasElision) { for (ElementNode* node = firstPutElement; node; node = node->next()) { if (node->elision()) { hasElision = true; break; } } } if (!hasElision) return generator.emitNewArrayWithSpread(generator.finalDestination(dst), m_element); } RefPtr array = newArray(generator.tempDestination(dst), m_element, length, hadVariableExpression); ElementNode* n = firstPutElement; for (; n; n = n->next()) { if (n->value()->isSpreadExpression()) goto handleSpread; RefPtr value = generator.emitNode(n->value()); length += n->elision(); RefPtr index = generator.emitLoad(nullptr, jsNumber(length++)); generator.emitDirectPutByVal(array.get(), index.get(), value.get()); } if (m_elision) { RegisterID* value = generator.emitLoad(nullptr, jsNumber(m_elision + length)); generator.emitPutById(array.get(), generator.propertyNames().length, value); } return generator.move(dst, array.get()); handleSpread: RefPtr index = generator.emitLoad(generator.newTemporary(), jsNumber(length)); auto spreader = scopedLambda([array, index](BytecodeGenerator& generator, RegisterID* value) { generator.emitDirectPutByVal(array.get(), index.get(), value); generator.emitInc(index.get()); }); for (; n; n = n->next()) { if (n->elision()) generator.emitBinaryOp(index.get(), index.get(), generator.emitLoad(nullptr, jsNumber(n->elision())), OperandTypes(ResultType::numberTypeIsInt32(), ResultType::numberTypeIsInt32())); if (n->value()->isSpreadExpression()) { SpreadExpressionNode* spread = static_cast(n->value()); generator.emitEnumeration(spread, spread->expression(), spreader); } else { generator.emitDirectPutByVal(array.get(), index.get(), generator.emitNode(n->value())); generator.emitInc(index.get()); } } if (m_elision) { generator.emitBinaryOp(index.get(), index.get(), generator.emitLoad(nullptr, jsNumber(m_elision)), OperandTypes(ResultType::numberTypeIsInt32(), ResultType::numberTypeIsInt32())); generator.emitPutById(array.get(), generator.propertyNames().length, index.get()); } return generator.move(dst, array.get()); } bool ArrayNode::isSimpleArray() const { if (m_elision) return false; for (ElementNode* ptr = m_element; ptr; ptr = ptr->next()) { if (ptr->elision()) return false; if (ptr->value()->isSpreadExpression()) return false; } return true; } ArgumentListNode* ArrayNode::toArgumentList(ParserArena& parserArena, int lineNumber, int startPosition) const { ASSERT(!m_elision); ElementNode* ptr = m_element; if (!ptr) return nullptr; JSTokenLocation location; location.line = lineNumber; location.startOffset = startPosition; ArgumentListNode* head = new (parserArena) ArgumentListNode(location, ptr->value()); ArgumentListNode* tail = head; ptr = ptr->next(); for (; ptr; ptr = ptr->next()) { ASSERT(!ptr->elision()); tail = new (parserArena) ArgumentListNode(location, tail, ptr->value()); } return head; } // ------------------------------ ObjectLiteralNode ---------------------------- RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (!m_list) { if (dst == generator.ignoredResult()) return nullptr; return generator.emitNewObject(generator.finalDestination(dst)); } RefPtr newObj = generator.emitNewObject(generator.tempDestination(dst)); generator.emitNode(newObj.get(), m_list); return generator.move(dst, newObj.get()); } // ------------------------------ PropertyListNode ----------------------------- static inline void emitPutHomeObject(BytecodeGenerator& generator, RegisterID* function, RegisterID* homeObject) { generator.emitPutById(function, generator.propertyNames().builtinNames().homeObjectPrivateName(), homeObject); } void PropertyListNode::emitDeclarePrivateFieldNames(BytecodeGenerator& generator, RegisterID* scope) { // Walk the list and declare any Private property names (e.g. `#foo`) in the provided scope. RefPtr createPrivateSymbol; for (PropertyListNode* p = this; p; p = p->m_next) { const PropertyNode& node = *p->m_node; if (node.type() & PropertyNode::Private) { if (!createPrivateSymbol) createPrivateSymbol = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::createPrivateSymbol); CallArguments arguments(generator, nullptr, 0); generator.emitLoad(arguments.thisRegister(), jsUndefined()); RefPtr symbol = generator.emitCall(generator.finalDestination(nullptr, createPrivateSymbol.get()), createPrivateSymbol.get(), NoExpectedFunction, arguments, position(), position(), position(), DebuggableCall::No); Variable var = generator.variable(*node.name()); generator.emitPutToScope(scope, var, symbol.get(), DoNotThrowIfNotFound, InitializationMode::ConstInitialization); } } } RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector* instanceFieldLocations, Vector* staticFieldLocations) { PropertyListNode* p = this; RegisterID* dst = nullptr; // Fast case: this loop just handles regular value properties. 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; } if (p->isStaticClassField()) { ASSERT(staticFieldLocations); staticFieldLocations->append(p->position()); continue; } emitPutConstantProperty(generator, dst, *p->m_node); } // Were there any get/set properties? if (p) { // Build a list of getter/setter pairs to try to put them at the same time. If we encounter // a computed property or a spread, just emit everything as that may override previous values. bool canOverrideProperties = false; typedef std::pair GetterSetterPair; typedef HashMap GetterSetterMap; GetterSetterMap instanceMap; GetterSetterMap staticMap; // Build a map, pairing get/set values together. for (PropertyListNode* q = p; q; q = q->m_next) { PropertyNode* node = q->m_node; if (node->m_type & PropertyNode::Computed || node->m_type & PropertyNode::Spread) { canOverrideProperties = true; break; } if (node->m_type & PropertyNode::Constant) continue; // Duplicates are possible. GetterSetterPair pair(node, static_cast(nullptr)); GetterSetterMap& map = node->isStaticClassProperty() ? staticMap : instanceMap; GetterSetterMap::AddResult result = map.add(node->name()->impl(), pair); auto& resultPair = result.iterator->value; if (!result.isNewEntry) { if (resultPair.first->m_type == node->m_type) { resultPair.first->setIsOverriddenByDuplicate(); resultPair.first = node; } else { if (resultPair.second) resultPair.second->setIsOverriddenByDuplicate(); resultPair.second = node; } } } // Iterate over the remaining properties in the list. for (; p; p = p->m_next) { 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; } if (p->isStaticClassField()) { ASSERT(staticFieldLocations); staticFieldLocations->append(p->position()); continue; } // Handle regular values. if (node->m_type & PropertyNode::Constant) { emitPutConstantProperty(generator, dst, *node); continue; } else if (node->m_type & PropertyNode::Spread) { generator.emitNode(dst, node->m_assign); continue; } RefPtr value = generator.emitNode(node->m_assign); bool needsSuperBinding = node->needsSuperBinding(); if (needsSuperBinding) emitPutHomeObject(generator, value.get(), dst); unsigned attributes = node->isClassProperty() ? (PropertyAttribute::Accessor | PropertyAttribute::DontEnum) : static_cast(PropertyAttribute::Accessor); ASSERT(node->m_type & (PropertyNode::Getter | PropertyNode::Setter)); // This is a get/set property which may be overridden by a computed property or spread later. if (canOverrideProperties) { // Computed accessors. if (node->m_type & PropertyNode::Computed) { RefPtr propertyName = generator.emitNode(node->m_expression); if (generator.shouldSetFunctionName(node->m_assign)) { propertyName = generator.emitToPropertyKey(generator.newTemporary(), propertyName.get()); generator.emitSetFunctionName(value.get(), propertyName.get()); } if (node->m_type & PropertyNode::Getter) generator.emitPutGetterByVal(dst, propertyName.get(), attributes, value.get()); else generator.emitPutSetterByVal(dst, propertyName.get(), attributes, value.get()); continue; } if (node->m_type & PropertyNode::Getter) generator.emitPutGetterById(dst, *node->name(), attributes, value.get()); else generator.emitPutSetterById(dst, *node->name(), attributes, value.get()); continue; } // This is a get/set property pair. GetterSetterMap& map = node->isStaticClassProperty() ? staticMap : instanceMap; GetterSetterMap::iterator it = map.find(node->name()->impl()); ASSERT(it != map.end()); GetterSetterPair& pair = it->value; // Was this already generated as a part of its partner? if (pair.second == node || node->isOverriddenByDuplicate()) continue; // Generate the paired node now. RefPtr getterReg; RefPtr setterReg; RegisterID* secondReg = nullptr; if (node->m_type & PropertyNode::Getter) { getterReg = value; if (pair.second) { ASSERT(pair.second->m_type & PropertyNode::Setter); setterReg = generator.emitNode(pair.second->m_assign); secondReg = setterReg.get(); } else { setterReg = generator.newTemporary(); generator.emitLoad(setterReg.get(), jsUndefined()); } } else { ASSERT(node->m_type & PropertyNode::Setter); setterReg = value; if (pair.second) { ASSERT(pair.second->m_type & PropertyNode::Getter); getterReg = generator.emitNode(pair.second->m_assign); secondReg = getterReg.get(); } else { getterReg = generator.newTemporary(); generator.emitLoad(getterReg.get(), jsUndefined()); } } ASSERT(!pair.second || needsSuperBinding == pair.second->needsSuperBinding()); if (needsSuperBinding && pair.second) emitPutHomeObject(generator, secondReg, dst); generator.emitPutGetterSetter(dst, *node->name(), attributes, getterReg.get(), setterReg.get()); } } return dstOrConstructor; } void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, RegisterID* newObj, PropertyNode& node) { // Private fields are handled in a synthetic classFieldInitializer function, not here. ASSERT(!(node.type() & PropertyNode::Private)); if (PropertyNode::isUnderscoreProtoSetter(generator.vm(), node)) { RefPtr prototype = generator.emitNode(node.m_assign); generator.emitDirectSetPrototypeOf(newObj, prototype.get()); return; } bool shouldSetFunctionName = generator.shouldSetFunctionName(node.m_assign); RefPtr propertyName; if (!node.name()) { propertyName = generator.newTemporary(); if (shouldSetFunctionName) generator.emitToPropertyKey(propertyName.get(), generator.emitNode(node.m_expression)); else generator.emitNode(propertyName.get(), node.m_expression); } RefPtr value = generator.emitNode(node.m_assign); if (node.needsSuperBinding()) emitPutHomeObject(generator, value.get(), newObj); if (node.isClassProperty()) { ASSERT(node.needsSuperBinding()); if (node.name()) propertyName = generator.emitLoad(nullptr, *node.name()); if (shouldSetFunctionName) generator.emitSetFunctionName(value.get(), propertyName.get()); generator.emitCallDefineProperty(newObj, propertyName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable, m_position); return; } if (const auto* identifier = node.name()) { ASSERT(!propertyName); Optional optionalIndex = parseIndex(*identifier); if (!optionalIndex) { generator.emitDirectPutById(newObj, *identifier, value.get()); return; } propertyName = generator.emitLoad(nullptr, jsNumber(optionalIndex.value())); generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); return; } if (shouldSetFunctionName) generator.emitSetFunctionName(value.get(), propertyName.get()); generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); } void PropertyListNode::emitSaveComputedFieldName(BytecodeGenerator& generator, PropertyNode& node) { ASSERT(node.isComputedClassField()); // The 'name' refers to a synthetic private name in the class scope, where the property key is saved for later use. const Identifier& description = *node.name(); Variable var = generator.variable(description); ASSERT(!var.local()); RefPtr propertyExpr = generator.emitNode(node.m_expression); RefPtr propertyName = generator.emitToPropertyKey(generator.newTemporary(), propertyExpr.get()); if (node.isStaticClassField()) { Ref