SpeculatedType speculationFromValue(JSValue value) { if (value.isEmpty()) return SpecEmpty; if (value.isInt32()) return SpecInt32; if (value.isDouble()) { double number = value.asNumber(); if (number != number) return SpecDoubleNaN; if (value.isMachineInt()) return SpecInt52AsDouble; return SpecNonIntAsDouble; } if (value.isCell()) return speculationFromCell(value.asCell()); if (value.isBoolean()) return SpecBoolean; ASSERT(value.isUndefinedOrNull()); return SpecOther; }
Arguments* StackVisitor::Frame::existingArguments() { if (codeBlock()->codeType() != FunctionCode) return 0; if (!codeBlock()->usesArguments()) return 0; VirtualRegister reg; #if ENABLE(DFG_JIT) if (isInlinedFrame()) reg = inlineCallFrame()->argumentsRegister; else #endif // ENABLE(DFG_JIT) reg = codeBlock()->argumentsRegister(); JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue(); if (!result || !result.isCell()) // Protect against Undefined in case we throw in op_enter. return 0; return jsCast<Arguments*>(result); }
void AbstractValue::set(Graph& graph, JSValue value) { if (!!value && value.isCell()) { m_currentKnownStructure.makeTop(); Structure* structure = value.asCell()->structure(); setFuturePossibleStructure(graph, structure); m_arrayModes = asArrayModes(structure->indexingType()); clobberArrayModes(); } else { m_currentKnownStructure.clear(); m_futurePossibleStructure.clear(); m_arrayModes = 0; } m_type = speculationFromValue(value); if (m_type == SpecInt52AsDouble) m_type = SpecInt52; m_value = value; checkConsistency(); }
ALWAYS_INLINE void SlotVisitor::internalAppend(JSValue* slot) { // This internalAppend is only intended for visits to object and array backing stores. // as it can change the JSValue pointed to be the argument when the original JSValue // is a string that contains the same contents as another string. StackStats::probe(); ASSERT(slot); JSValue value = *slot; ASSERT(value); if (!value.isCell()) return; JSCell* cell = value.asCell(); if (!cell) return; validate(cell); if (m_shouldHashCons && cell->isString()) { JSString* string = jsCast<JSString*>(cell); if (string->shouldTryHashCons() && string->tryHashConsLock()) { UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value); if (addResult.isNewEntry) string->setHashConsSingleton(); else { JSValue existingJSValue = addResult.iterator->value; if (value != existingJSValue) jsCast<JSString*>(existingJSValue.asCell())->clearHashConsSingleton(); *slot = existingJSValue; string->releaseHashConsLock(); return; } string->releaseHashConsLock(); } } internalAppend(cell); }
EncodedJSValue operationGetByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) { JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); if (LIKELY(baseValue.isCell())) { JSCell* base = baseValue.asCell(); if (property.isUInt32()) { JSGlobalData* globalData = &exec->globalData(); uint32_t i = property.asUInt32(); // FIXME: the JIT used to handle these in compiled code! if (isJSArray(globalData, base) && asArray(base)->canGetIndex(i)) return JSValue::encode(asArray(base)->getIndex(i)); // FIXME: the JITstub used to relink this to an optimized form! if (isJSString(globalData, base) && asString(base)->canGetIndex(i)) return JSValue::encode(asString(base)->getIndex(exec, i)); // FIXME: the JITstub used to relink this to an optimized form! if (isJSByteArray(globalData, base) && asByteArray(base)->canAccessIndex(i)) return JSValue::encode(asByteArray(base)->getIndex(exec, i)); return JSValue::encode(baseValue.get(exec, i)); } if (property.isString()) { Identifier propertyName(exec, asString(property)->value(exec)); PropertySlot slot(base); if (base->fastGetOwnPropertySlot(exec, propertyName, slot)) return JSValue::encode(slot.getValue(exec, propertyName)); } } Identifier ident(exec, property.toString(exec)); return JSValue::encode(baseValue.get(exec, ident)); }
EncodedJSValue JIT_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); if (LIKELY(baseValue.isCell())) { JSCell* base = baseValue.asCell(); if (property.isUInt32()) { return getByVal(exec, base, property.asUInt32()); } else if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble); if (propertyAsUInt32 == propertyAsDouble && isIndex(propertyAsUInt32)) return getByVal(exec, base, propertyAsUInt32); } else if (property.isString()) { Structure& structure = *base->structure(vm); if (JSCell::canUseFastGetOwnProperty(structure)) { if (AtomicStringImpl* existingAtomicString = asString(property)->toExistingAtomicString(exec)) { if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomicString)) return JSValue::encode(result); } } } } baseValue.requireObjectCoercible(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); auto propertyName = property.toPropertyKey(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); return JSValue::encode(baseValue.get(exec, propertyName)); }
EncodedJSValue DFG_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) { JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); if (LIKELY(baseValue.isCell())) { JSCell* base = baseValue.asCell(); if (property.isUInt32()) { return getByVal(exec, base, property.asUInt32()); } else if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble); if (propertyAsUInt32 == propertyAsDouble) return getByVal(exec, base, propertyAsUInt32); } else if (property.isString()) { if (JSValue result = base->fastGetOwnProperty(exec, asString(property)->value(exec))) return JSValue::encode(result); } } Identifier ident(exec, property.toString(exec)); return JSValue::encode(baseValue.get(exec, ident)); }
InferredType::Descriptor InferredType::Descriptor::forValue(JSValue value) { if (value.isBoolean()) return Boolean; if (value.isUndefinedOrNull()) return Other; if (value.isInt32()) return Int32; if (value.isNumber()) return Number; if (value.isCell()) { JSCell* cell = value.asCell(); if (cell->isString()) return String; if (cell->isSymbol()) return Symbol; if (cell->isObject()) { if (cell->structure()->transitionWatchpointSetIsStillValid()) return Descriptor(ObjectWithStructure, cell->structure()); return Object; } } return Top; }
void emitPutByOffset(unsigned indexInBlock, Node* node, Structure* structure, const PutByIdVariant& variant, unsigned identifierNumber) { NodeOrigin origin = node->origin; Edge childEdge = node->child1(); Node* child = childEdge.node(); ASSERT(variant.oldStructure() == structure); bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell; // Now before we do anything else, push the CFA forward over the PutById // and make sure we signal to the loop that it should continue and not // do any eliminations. m_interpreter.execute(indexInBlock); if (needsWatchpoint) { m_insertionSet.insertNode( indexInBlock, SpecNone, StructureTransitionWatchpoint, origin, OpInfo(structure), childEdge); } else if (needsCellCheck) { m_insertionSet.insertNode( indexInBlock, SpecNone, Phantom, origin, childEdge); } childEdge.setUseKind(KnownCellUse); StructureTransitionData* transitionData = 0; if (variant.kind() == PutByIdVariant::Transition) { transitionData = m_graph.addStructureTransitionData( StructureTransitionData(structure, variant.newStructure())); if (node->op() == PutById) { if (!structure->storedPrototype().isNull()) { addStructureTransitionCheck( origin, indexInBlock, structure->storedPrototype().asCell()); } m_graph.chains().addLazily(variant.structureChain()); for (unsigned i = 0; i < variant.structureChain()->size(); ++i) { JSValue prototype = variant.structureChain()->at(i)->storedPrototype(); if (prototype.isNull()) continue; ASSERT(prototype.isCell()); addStructureTransitionCheck( origin, indexInBlock, prototype.asCell()); } } } Edge propertyStorage; if (isInlineOffset(variant.offset())) propertyStorage = childEdge; else if ( variant.kind() == PutByIdVariant::Replace || structure->outOfLineCapacity() == variant.newStructure()->outOfLineCapacity()) { propertyStorage = Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge)); } else if (!structure->outOfLineCapacity()) { ASSERT(variant.newStructure()->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* allocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, AllocatePropertyStorage, origin, OpInfo(transitionData), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(allocatePropertyStorage); } else { ASSERT(structure->outOfLineCapacity()); ASSERT(variant.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* reallocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, ReallocatePropertyStorage, origin, OpInfo(transitionData), childEdge, Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge))); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(reallocatePropertyStorage); } if (variant.kind() == PutByIdVariant::Transition) { Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transitionData), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); m_insertionSet.insert(indexInBlock, putStructure); } node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage); m_insertionSet.insertNode( indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child2().node(), KnownCellUse)); StorageAccessData storageAccessData; storageAccessData.offset = variant.offset(); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); }
EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncIsTypedArrayView(ExecState* exec) { JSValue value = exec->uncheckedArgument(0); return JSValue::encode(jsBoolean(value.isCell() && isTypedView(value.asCell()->classInfo()->typedArrayStorageType))); }
bool run() { ASSERT(m_graph.m_form == ThreadedCPS); for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; variable->clearVotes(); } // Identify the set of variables that are always subject to the same structure // checks. For now, only consider monomorphic structure checks (one structure). for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { Node* node = block->at(indexInBlock); switch (node->op()) { case CheckStructure: case StructureTransitionWatchpoint: { Node* child = node->child1().node(); if (child->op() != GetLocal) break; VariableAccessData* variable = child->variableAccessData(); variable->vote(VoteStructureCheck); if (!shouldConsiderForHoisting(variable)) break; noticeStructureCheck(variable, node->structureSet()); break; } case ForwardCheckStructure: case ForwardStructureTransitionWatchpoint: // We currently rely on the fact that we're the only ones who would // insert this node. RELEASE_ASSERT_NOT_REACHED(); break; case GetByOffset: case PutByOffset: case PutStructure: case AllocatePropertyStorage: case ReallocatePropertyStorage: case GetButterfly: case GetByVal: case PutByVal: case PutByValAlias: case GetArrayLength: case CheckArray: case GetIndexedPropertyStorage: case Phantom: // Don't count these uses. break; case ArrayifyToStructure: case Arrayify: if (node->arrayMode().conversion() == Array::RageConvert) { // Rage conversion changes structures. We should avoid tying to do // any kind of hoisting when rage conversion is in play. Node* child = node->child1().node(); if (child->op() != GetLocal) break; VariableAccessData* variable = child->variableAccessData(); variable->vote(VoteOther); if (!shouldConsiderForHoisting(variable)) break; noticeStructureCheck(variable, 0); } break; case SetLocal: { // Find all uses of the source of the SetLocal. If any of them are a // kind of CheckStructure, then we should notice them to ensure that // we're not hoisting a check that would contravene checks that are // already being performed. VariableAccessData* variable = node->variableAccessData(); if (!shouldConsiderForHoisting(variable)) break; Node* source = node->child1().node(); for (unsigned subIndexInBlock = 0; subIndexInBlock < block->size(); ++subIndexInBlock) { Node* subNode = block->at(subIndexInBlock); switch (subNode->op()) { case CheckStructure: { if (subNode->child1() != source) break; noticeStructureCheck(variable, subNode->structureSet()); break; } case StructureTransitionWatchpoint: { if (subNode->child1() != source) break; noticeStructureCheck(variable, subNode->structure()); break; } default: break; } } m_graph.voteChildren(node, VoteOther); break; } case GarbageValue: break; default: m_graph.voteChildren(node, VoteOther); break; } } } // Disable structure hoisting on variables that appear to mostly be used in // contexts where it doesn't make sense. for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting()) continue; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the ratio is ", variable->voteRatio(), ".\n"); #endif iter->value.m_structure = 0; } // Disable structure check hoisting for variables that cross the OSR entry that // we're currently taking, and where the value currently does not have the // structure we want. for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; ASSERT(block->isReachable); if (!block->isOSRTarget) continue; if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex) continue; for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) { int operand = m_graph.m_mustHandleValues.operandForIndex(i); Node* node = block->variablesAtHead.operand(operand); if (!node) continue; VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; if (!iter->value.m_structure) continue; JSValue value = m_graph.m_mustHandleValues[i]; if (!value || !value.isCell()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the OSR entry value is not a cell: ", value, ".\n"); #endif iter->value.m_structure = 0; continue; } if (value.asCell()->structure() != iter->value.m_structure) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the OSR entry value has structure ", RawPointer(value.asCell()->structure()), " and we wanted ", RawPointer(iter->value.m_structure), ".\n"); #endif iter->value.m_structure = 0; continue; } } } bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->value.m_structure) { dataLog( "Not hoisting checks for ", VariableAccessDataDump(m_graph, it->key), " because of heuristics.\n"); continue; } dataLog("Hoisting checks for ", VariableAccessDataDump(m_graph, it->key), "\n"); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) // Place CheckStructure's at SetLocal sites. InsertionSet insertionSet(m_graph); for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { Node* node = block->at(indexInBlock); // Be careful not to use 'node' after appending to the graph. In those switch // cases where we need to append, we first carefully extract everything we need // from the node, before doing any appending. switch (node->op()) { case SetArgument: { ASSERT(!blockIndex); // Insert a GetLocal and a CheckStructure immediately following this // SetArgument, if the variable was a candidate for structure hoisting. // If the basic block previously only had the SetArgument as its // variable-at-tail, then replace it with this GetLocal. VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->value.m_structure) break; CodeOrigin codeOrigin = node->codeOrigin; Node* getLocal = insertionSet.insertNode( indexInBlock + 1, variable->prediction(), GetLocal, codeOrigin, OpInfo(variable), Edge(node)); insertionSet.insertNode( indexInBlock + 1, SpecNone, CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)), Edge(getLocal, CellUse)); if (block->variablesAtTail.operand(variable->local()) == node) block->variablesAtTail.operand(variable->local()) = getLocal; m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocal); changed = true; break; } case SetLocal: { VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->value.m_structure) break; // First insert a dead SetLocal to tell OSR that the child's value should // be dropped into this bytecode variable if the CheckStructure decides // to exit. CodeOrigin codeOrigin = node->codeOrigin; Edge child1 = node->child1(); insertionSet.insertNode( indexInBlock, SpecNone, SetLocal, codeOrigin, OpInfo(variable), child1); // Use a ForwardCheckStructure to indicate that we should exit to the // next bytecode instruction rather than reexecuting the current one. insertionSet.insertNode( indexInBlock, SpecNone, ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)), Edge(child1.node(), CellUse)); changed = true; break; } default: break; } } insertionSet.execute(block); } return changed; }
bool CallFrame::hasActivation() const { JSValue activation = uncheckedActivation(); return !!activation && activation.isCell(); }
EncodedJSValue JSC_HOST_CALL privateFuncIsMap(ExecState* exec) { JSValue value = exec->uncheckedArgument(0); return JSValue::encode(jsBoolean(value.isCell() && value.asCell()->type() == JSMapType)); }
bool dumpIfTerminal(JSValue value) { if (!value.isCell()) { dumpImmediate(value); return true; } if (value.isString()) { UString str = asString(value)->value(m_exec); dumpString(str); return true; } if (value.isNumber()) { write(DoubleTag); write(value.asNumber()); return true; } if (value.isObject() && asObject(value)->inherits(&DateInstance::s_info)) { write(DateTag); write(asDateInstance(value)->internalNumber()); return true; } if (isArray(value)) return false; // Object cannot be serialized because the act of walking the object creates new objects if (value.isObject() && asObject(value)->inherits(&JSNavigator::s_info)) { fail(); write(NullTag); return true; } if (value.isObject()) { JSObject* obj = asObject(value); if (obj->inherits(&JSFile::s_info)) { write(FileTag); write(toFile(obj)); return true; } if (obj->inherits(&JSFileList::s_info)) { FileList* list = toFileList(obj); write(FileListTag); unsigned length = list->length(); write(length); for (unsigned i = 0; i < length; i++) write(list->item(i)); return true; } if (obj->inherits(&JSBlob::s_info)) { write(BlobTag); Blob* blob = toBlob(obj); write(blob->url()); write(blob->type()); write(blob->size()); return true; } if (obj->inherits(&JSImageData::s_info)) { ImageData* data = toImageData(obj); write(ImageDataTag); write(data->width()); write(data->height()); write(data->data()->length()); write(data->data()->data()->data(), data->data()->length()); return true; } if (obj->inherits(&RegExpObject::s_info)) { RegExpObject* regExp = asRegExpObject(obj); char flags[3]; int flagCount = 0; if (regExp->regExp()->global()) flags[flagCount++] = 'g'; if (regExp->regExp()->ignoreCase()) flags[flagCount++] = 'i'; if (regExp->regExp()->multiline()) flags[flagCount++] = 'm'; write(RegExpTag); write(regExp->regExp()->pattern()); write(UString(flags, flagCount)); return true; } if (obj->inherits(&JSMessagePort::s_info)) { ObjectPool::iterator index = m_transferredMessagePorts.find(obj); if (index != m_transferredMessagePorts.end()) { write(MessagePortReferenceTag); uint32_t i = index->second; write(i); return true; } return false; } CallData unusedData; if (getCallData(value, unusedData) == CallTypeNone) return false; } // Any other types are expected to serialize as null. write(NullTag); return true; }
void SlotVisitor::append(JSValue value) { if (!value || !value.isCell()) return; setMarkedAndAppendToMarkStack(value.asCell()); }