Changeset 128832 in webkit
- Timestamp:
- Sep 17, 2012 6:15:04 PM (12 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r128822 r128832 1 2012-09-17 Geoffrey Garen <ggaren@apple.com> 2 3 Refactored the arguments object so it doesn't dictate closure layout 4 https://bugs.webkit.org/show_bug.cgi?id=96955 5 6 Reviewed by Oliver Hunt. 7 8 * bytecode/CodeBlock.h: 9 (JSC::ExecState::argumentAfterCapture): Helper function for accessing an 10 argument that has been moved for capture. 11 12 * bytecompiler/BytecodeGenerator.cpp: 13 (JSC::BytecodeGenerator::BytecodeGenerator): Generate metadata for arguments 14 that are captured. We don't move any arguments yet, but we do use this 15 metadata to tell the arguments object if an argument is stored in the 16 activation. 17 18 * dfg/DFGOperations.cpp: 19 * dfg/DFGSpeculativeJIT.cpp: 20 (JSC::DFG::SpeculativeJIT::compileGetByValOnArguments): 21 (JSC::DFG::SpeculativeJIT::compileGetArgumentsLength): 22 * dfg/DFGSpeculativeJIT64.cpp: 23 (JSC::DFG::SpeculativeJIT::compile): Updated for the arguments object not 24 malloc'ing a separate backing store, and for a rename from deletedArguments 25 to slowArguments. 26 27 * interpreter/CallFrame.h: 28 (ExecState): 29 * interpreter/Interpreter.cpp: 30 (JSC::Interpreter::unwindCallFrame): 31 (JSC::Interpreter::privateExecute): 32 * jit/JITStubs.cpp: 33 (JSC::DEFINE_STUB_FUNCTION): 34 * llint/LLIntSlowPaths.cpp: 35 (JSC::LLInt::LLINT_SLOW_PATH_DECL): Updated for small interface changes. 36 37 * runtime/Arguments.cpp: 38 (JSC::Arguments::visitChildren): 39 (JSC::Arguments::copyToArguments): 40 (JSC::Arguments::fillArgList): 41 (JSC::Arguments::getOwnPropertySlotByIndex): 42 (JSC::Arguments::createStrictModeCallerIfNecessary): 43 (JSC::Arguments::createStrictModeCalleeIfNecessary): 44 (JSC::Arguments::getOwnPropertySlot): 45 (JSC::Arguments::getOwnPropertyDescriptor): 46 (JSC::Arguments::getOwnPropertyNames): 47 (JSC::Arguments::putByIndex): 48 (JSC::Arguments::put): 49 (JSC::Arguments::deletePropertyByIndex): 50 (JSC::Arguments::deleteProperty): 51 (JSC::Arguments::defineOwnProperty): 52 (JSC::Arguments::tearOff): Moved all data inline into the object, for speed, 53 and refactored all internal argument accesses to use helper functions, so 54 we can change the implementation without changing lots of code. 55 56 (JSC::Arguments::didTearOffActivation): This function needs to account 57 for arguments that were moved by the activation object. We do this accounting 58 through a side vector that tells us where our arguments will be in the 59 activation. 60 61 (JSC::Arguments::tearOffForInlineCallFrame): 62 * runtime/Arguments.h: 63 (Arguments): 64 (JSC::Arguments::length): 65 (JSC::Arguments::isTornOff): 66 (JSC::Arguments::Arguments): 67 (JSC::Arguments::allocateSlowArguments): 68 (JSC::Arguments::tryDeleteArgument): 69 (JSC::Arguments::trySetArgument): 70 (JSC::Arguments::tryGetArgument): 71 (JSC::Arguments::isDeletedArgument): 72 (JSC::Arguments::isArgument): 73 (JSC::Arguments::argument): 74 (JSC::Arguments::finishCreation): 75 76 * runtime/JSActivation.h: 77 (JSC::JSActivation::create): 78 (JSActivation): 79 (JSC::JSActivation::captureStart): 80 (JSC::JSActivation::storageSize): 81 (JSC::JSActivation::registerOffset): 82 (JSC::JSActivation::isValid): The activation object is no longer responsible 83 for copying extra arguments provided by the caller. The argumnents object 84 does this instead. This means we can allocate and initialize an activation 85 without worrying about the call frame's argument count. 86 87 * runtime/SymbolTable.h: 88 (JSC::SlowArgument::SlowArgument): 89 (SlowArgument): 90 (JSC): 91 (JSC::SharedSymbolTable::parameterCount): 92 (SharedSymbolTable): 93 (JSC::SharedSymbolTable::slowArguments): 94 (JSC::SharedSymbolTable::setSlowArguments): Added data structures to back 95 the algorithms above. 96 1 97 2012-09-17 Filip Pizlo <fpizlo@apple.com> 2 98 -
trunk/Source/JavaScriptCore/bytecode/CodeBlock.h
r128544 r128832 1545 1545 #endif 1546 1546 1547 inline JSValue ExecState::argumentAfterCapture(size_t argument) 1548 { 1549 if (argument >= argumentCount()) 1550 return jsUndefined(); 1551 1552 if (!codeBlock()) 1553 return this[argumentOffset(argument)].jsValue(); 1554 1555 if (argument >= static_cast<size_t>(codeBlock()->symbolTable()->parameterCount())) 1556 return this[argumentOffset(argument)].jsValue(); 1557 1558 const SlowArgument* slowArguments = codeBlock()->symbolTable()->slowArguments(); 1559 if (!slowArguments || slowArguments[argument].status == SlowArgument::Normal) 1560 return this[argumentOffset(argument)].jsValue(); 1561 1562 ASSERT(slowArguments[argument].status == SlowArgument::Captured); 1563 return this[slowArguments[argument].indexIfCaptured].jsValue(); 1564 } 1565 1547 1566 #if ENABLE(DFG_JIT) 1548 1567 inline void DFGCodeBlocks::mark(void* candidateCodeBlock) -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r128534 r128832 424 424 } 425 425 426 bool capturesAnyArgument = codeBlock->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks; // May reify arguments object. 427 if (!capturesAnyArgument && functionBody->hasCapturedVariables()) { 426 bool mayReifyArgumentsObject = codeBlock->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks; 427 bool capturesAnyArgumentByName = false; 428 if (functionBody->hasCapturedVariables()) { 428 429 FunctionParameters& parameters = *functionBody->parameters(); 429 430 for (size_t i = 0; i < parameters.size(); ++i) { 430 431 if (!functionBody->captures(parameters[i])) 431 432 continue; 432 capturesAnyArgument = true;433 capturesAnyArgumentByName = true; 433 434 break; 434 435 } 435 436 } 436 437 437 if ( capturesAnyArgument) {438 if (mayReifyArgumentsObject || capturesAnyArgumentByName) { 438 439 symbolTable->setCaptureMode(SharedSymbolTable::AllOfTheThings); 439 440 symbolTable->setCaptureStart(-CallFrame::offsetFor(symbolTable->parameterCountIncludingThis())); … … 441 442 symbolTable->setCaptureMode(SharedSymbolTable::SomeOfTheThings); 442 443 symbolTable->setCaptureStart(m_codeBlock->m_numVars); 444 } 445 446 if (mayReifyArgumentsObject && capturesAnyArgumentByName) { 447 size_t parameterCount = symbolTable->parameterCount(); 448 OwnArrayPtr<SlowArgument> slowArguments = adoptArrayPtr(new SlowArgument[parameterCount]); 449 for (size_t i = 0; i < parameterCount; ++i) { 450 slowArguments[i].status = SlowArgument::Captured; 451 slowArguments[i].indexIfCaptured = CallFrame::argumentOffset(i); 452 } 453 symbolTable->setSlowArguments(slowArguments.release()); 443 454 } 444 455 … … 682 693 if (entry.isNull()) 683 694 return false; 684 695 685 696 if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) 686 697 return true; -
trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp
r128400 r128832 1179 1179 ASSERT(exec->codeBlock()->usesArguments()); 1180 1180 if (activationCell) { 1181 jsCast<Arguments*>(argumentsCell)->didTearOffActivation(exec ->globalData(), jsCast<JSActivation*>(activationCell));1181 jsCast<Arguments*>(argumentsCell)->didTearOffActivation(exec, jsCast<JSActivation*>(activationCell)); 1182 1182 return; 1183 1183 } -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
r128802 r128832 3058 3058 ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); 3059 3059 3060 m_jit.loadPtr(3061 MacroAssembler::Address(baseReg, Arguments::offsetOfData()),3062 scratchReg);3063 3064 3060 // Two really lame checks. 3065 3061 speculationCheck( … … 3067 3063 m_jit.branchPtr( 3068 3064 MacroAssembler::AboveOrEqual, propertyReg, 3069 MacroAssembler::Address( scratchReg, OBJECT_OFFSETOF(ArgumentsData,numArguments))));3065 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_numArguments)))); 3070 3066 speculationCheck( 3071 3067 Uncountable, JSValueSource(), NoNode, … … 3073 3069 MacroAssembler::NonZero, 3074 3070 MacroAssembler::Address( 3075 scratchReg, OBJECT_OFFSETOF(ArgumentsData, deletedArguments))));3071 baseReg, OBJECT_OFFSETOF(Arguments, m_slowArguments)))); 3076 3072 3077 3073 m_jit.move(propertyReg, resultReg); … … 3079 3075 m_jit.signExtend32ToPtr(resultReg, resultReg); 3080 3076 m_jit.loadPtr( 3081 MacroAssembler::Address( scratchReg, OBJECT_OFFSETOF(ArgumentsData,registers)),3077 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_registers)), 3082 3078 scratchReg); 3083 3079 … … 3119 3115 ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); 3120 3116 3121 m_jit.loadPtr(3122 MacroAssembler::Address(baseReg, Arguments::offsetOfData()),3123 resultReg);3124 3125 3117 speculationCheck( 3126 3118 Uncountable, JSValueSource(), NoNode, 3127 3119 m_jit.branchTest8( 3128 3120 MacroAssembler::NonZero, 3129 MacroAssembler::Address( resultReg, OBJECT_OFFSETOF(ArgumentsData,overrodeLength))));3121 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_overrodeLength)))); 3130 3122 3131 3123 m_jit.load32( 3132 MacroAssembler::Address( resultReg, OBJECT_OFFSETOF(ArgumentsData,numArguments)),3124 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_numArguments)), 3133 3125 resultReg); 3134 3126 integerResult(resultReg, m_compileIndex); -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
r128802 r128832 2811 2811 return; 2812 2812 2813 m_jit.loadPtr(2814 MacroAssembler::Address(baseReg, Arguments::offsetOfData()),2815 scratchReg);2816 2817 2813 // Two really lame checks. 2818 2814 speculationCheck( … … 2820 2816 m_jit.branchPtr( 2821 2817 MacroAssembler::AboveOrEqual, propertyReg, 2822 MacroAssembler::Address( scratchReg, OBJECT_OFFSETOF(ArgumentsData,numArguments))));2818 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_numArguments)))); 2823 2819 speculationCheck( 2824 2820 Uncountable, JSValueSource(), NoNode, … … 2826 2822 MacroAssembler::NonZero, 2827 2823 MacroAssembler::Address( 2828 scratchReg, OBJECT_OFFSETOF(ArgumentsData, deletedArguments))));2824 baseReg, OBJECT_OFFSETOF(Arguments, m_slowArguments)))); 2829 2825 2830 2826 m_jit.move(propertyReg, scratch2Reg); … … 2832 2828 m_jit.signExtend32ToPtr(scratch2Reg, scratch2Reg); 2833 2829 m_jit.loadPtr( 2834 MacroAssembler::Address( scratchReg, OBJECT_OFFSETOF(ArgumentsData,registers)),2830 MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_registers)), 2835 2831 scratchReg); 2836 2832 -
trunk/Source/JavaScriptCore/interpreter/CallFrame.h
r128260 r128832 188 188 Register& uncheckedR(int); 189 189 190 // Access to arguments .190 // Access to arguments as passed. (After capture, arguments may move to a different location.) 191 191 size_t argumentCount() const { return argumentCountIncludingThis() - 1; } 192 192 size_t argumentCountIncludingThis() const { return this[RegisterFile::ArgumentCount].payload(); } … … 208 208 JSValue thisValue() { return this[thisArgumentOffset()].jsValue(); } 209 209 void setThisValue(JSValue value) { this[thisArgumentOffset()] = value; } 210 211 JSValue argumentAfterCapture(size_t argument); 210 212 211 213 static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + RegisterFile::CallFrameHeaderSize; } -
trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp
r128611 r128832 474 474 if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) { 475 475 if (activation) 476 jsCast<Arguments*>(arguments)->didTearOffActivation(callFrame ->globalData(), jsCast<JSActivation*>(activation));476 jsCast<Arguments*>(arguments)->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); 477 477 else 478 478 jsCast<Arguments*>(arguments)->tearOff(callFrame); … … 4509 4509 if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { 4510 4510 if (JSValue activationValue = callFrame->r(activation).jsValue()) 4511 asArguments(argumentsValue)->didTearOffActivation(callFrame ->globalData(), asActivation(activationValue));4511 asArguments(argumentsValue)->didTearOffActivation(callFrame, asActivation(activationValue)); 4512 4512 else 4513 4513 asArguments(argumentsValue)->tearOff(callFrame); -
trunk/Source/JavaScriptCore/jit/JITStubs.cpp
r128400 r128832 2324 2324 Arguments* arguments = jsCast<Arguments*>(stackFrame.args[0].jsValue()); 2325 2325 if (JSValue activationValue = stackFrame.args[1].jsValue()) { 2326 arguments->didTearOffActivation(callFrame ->globalData(), jsCast<JSActivation*>(activationValue));2326 arguments->didTearOffActivation(callFrame, jsCast<JSActivation*>(activationValue)); 2327 2327 return; 2328 2328 } -
trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
r128400 r128832 1476 1476 Arguments* arguments = jsCast<Arguments*>(exec->uncheckedR(unmodifiedArgumentsRegister(pc[1].u.operand)).jsValue()); 1477 1477 if (JSValue activationValue = LLINT_OP_C(2).jsValue()) 1478 arguments->didTearOffActivation( globalData, jsCast<JSActivation*>(activationValue));1478 arguments->didTearOffActivation(exec, jsCast<JSActivation*>(activationValue)); 1479 1479 else 1480 1480 arguments->tearOff(exec); -
trunk/Source/JavaScriptCore/runtime/Arguments.cpp
r128813 r128832 46 46 JSObject::visitChildren(thisObject, visitor); 47 47 48 if (thisObject-> d->registerArray)49 visitor.appendValues(thisObject-> d->registerArray.get(), thisObject->d->numArguments);50 visitor.append(&thisObject-> d->callee);51 visitor.append(&thisObject-> d->activation);48 if (thisObject->m_registerArray) 49 visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_numArguments); 50 visitor.append(&thisObject->m_callee); 51 visitor.append(&thisObject->m_activation); 52 52 } 53 53 … … 59 59 void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) 60 60 { 61 if (UNLIKELY( d->overrodeLength)) {61 if (UNLIKELY(m_overrodeLength)) { 62 62 length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length); 63 63 for (unsigned i = 0; i < length; i++) … … 67 67 ASSERT(length == this->length(exec)); 68 68 for (size_t i = 0; i < length; ++i) { 69 if ( !d->deletedArguments || !d->deletedArguments[i])70 callFrame->setArgument(i, argument(i).get());69 if (JSValue value = tryGetArgument(i)) 70 callFrame->setArgument(i, value); 71 71 else 72 72 callFrame->setArgument(i, get(exec, i)); … … 76 76 void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 77 77 { 78 if (UNLIKELY( d->overrodeLength)) {78 if (UNLIKELY(m_overrodeLength)) { 79 79 unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec); 80 80 for (unsigned i = 0; i < length; i++) … … 84 84 uint32_t length = this->length(exec); 85 85 for (size_t i = 0; i < length; ++i) { 86 if ( !d->deletedArguments || !d->deletedArguments[i])87 args.append( argument(i).get());86 if (JSValue value = tryGetArgument(i)) 87 args.append(value); 88 88 else 89 89 args.append(get(exec, i)); … … 94 94 { 95 95 Arguments* thisObject = jsCast<Arguments*>(cell); 96 if ( i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) {97 slot.setValue( thisObject->argument(i).get());96 if (JSValue value = thisObject->tryGetArgument(i)) { 97 slot.setValue(value); 98 98 return true; 99 99 } … … 104 104 void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) 105 105 { 106 if ( d->overrodeCaller)107 return; 108 109 d->overrodeCaller = true;106 if (m_overrodeCaller) 107 return; 108 109 m_overrodeCaller = true; 110 110 PropertyDescriptor descriptor; 111 111 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Accessor); … … 115 115 void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) 116 116 { 117 if ( d->overrodeCallee)118 return; 119 120 d->overrodeCallee = true;117 if (m_overrodeCallee) 118 return; 119 120 m_overrodeCallee = true; 121 121 PropertyDescriptor descriptor; 122 122 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Accessor); … … 128 128 Arguments* thisObject = jsCast<Arguments*>(cell); 129 129 unsigned i = propertyName.asIndex(); 130 if ( i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) {130 if (JSValue value = thisObject->tryGetArgument(i)) { 131 131 ASSERT(i < PropertyName::NotAnIndex); 132 slot.setValue( thisObject->argument(i).get());133 return true; 134 } 135 136 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject-> d->overrodeLength)) {137 slot.setValue(jsNumber(thisObject-> d->numArguments));138 return true; 139 } 140 141 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject-> d->overrodeCallee)) {142 if (!thisObject-> d->isStrictMode) {143 slot.setValue(thisObject-> d->callee.get());132 slot.setValue(value); 133 return true; 134 } 135 136 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) { 137 slot.setValue(jsNumber(thisObject->m_numArguments)); 138 return true; 139 } 140 141 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) { 142 if (!thisObject->m_isStrictMode) { 143 slot.setValue(thisObject->m_callee.get()); 144 144 return true; 145 145 } … … 147 147 } 148 148 149 if (propertyName == exec->propertyNames().caller && thisObject-> d->isStrictMode)149 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 150 150 thisObject->createStrictModeCallerIfNecessary(exec); 151 151 … … 157 157 Arguments* thisObject = jsCast<Arguments*>(object); 158 158 unsigned i = propertyName.asIndex(); 159 if ( i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) {159 if (JSValue value = thisObject->tryGetArgument(i)) { 160 160 ASSERT(i < PropertyName::NotAnIndex); 161 descriptor.setDescriptor( thisObject->argument(i).get(), None);162 return true; 163 } 164 165 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject-> d->overrodeLength)) {166 descriptor.setDescriptor(jsNumber(thisObject-> d->numArguments), DontEnum);167 return true; 168 } 169 170 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject-> d->overrodeCallee)) {171 if (!thisObject-> d->isStrictMode) {172 descriptor.setDescriptor(thisObject-> d->callee.get(), DontEnum);161 descriptor.setDescriptor(value, None); 162 return true; 163 } 164 165 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) { 166 descriptor.setDescriptor(jsNumber(thisObject->m_numArguments), DontEnum); 167 return true; 168 } 169 170 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) { 171 if (!thisObject->m_isStrictMode) { 172 descriptor.setDescriptor(thisObject->m_callee.get(), DontEnum); 173 173 return true; 174 174 } … … 176 176 } 177 177 178 if (propertyName == exec->propertyNames().caller && thisObject-> d->isStrictMode)178 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 179 179 thisObject->createStrictModeCallerIfNecessary(exec); 180 180 … … 185 185 { 186 186 Arguments* thisObject = jsCast<Arguments*>(object); 187 for (unsigned i = 0; i < thisObject->d->numArguments; ++i) { 188 if (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) 189 propertyNames.add(Identifier(exec, String::number(i))); 187 for (unsigned i = 0; i < thisObject->m_numArguments; ++i) { 188 if (!thisObject->isArgument(i)) 189 continue; 190 propertyNames.add(Identifier(exec, String::number(i))); 190 191 } 191 192 if (mode == IncludeDontEnumProperties) { … … 199 200 { 200 201 Arguments* thisObject = jsCast<Arguments*>(cell); 201 if (i < static_cast<unsigned>(thisObject->d->numArguments) && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { 202 thisObject->argument(i).set(exec->globalData(), thisObject, value); 203 return; 204 } 202 if (thisObject->trySetArgument(exec->globalData(), i, value)) 203 return; 205 204 206 205 PutPropertySlot slot(shouldThrow); … … 212 211 Arguments* thisObject = jsCast<Arguments*>(cell); 213 212 unsigned i = propertyName.asIndex(); 214 if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { 215 ASSERT(i < PropertyName::NotAnIndex); 216 thisObject->argument(i).set(exec->globalData(), thisObject, value); 217 return; 218 } 219 220 if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { 221 thisObject->d->overrodeLength = true; 213 if (thisObject->trySetArgument(exec->globalData(), i, value)) 214 return; 215 216 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 217 thisObject->m_overrodeLength = true; 222 218 thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); 223 219 return; 224 220 } 225 221 226 if (propertyName == exec->propertyNames().callee && !thisObject-> d->overrodeCallee) {227 if (!thisObject-> d->isStrictMode) {228 thisObject-> d->overrodeCallee = true;222 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 223 if (!thisObject->m_isStrictMode) { 224 thisObject->m_overrodeCallee = true; 229 225 thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); 230 226 return; … … 233 229 } 234 230 235 if (propertyName == exec->propertyNames().caller && thisObject-> d->isStrictMode)231 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 236 232 thisObject->createStrictModeCallerIfNecessary(exec); 237 233 … … 242 238 { 243 239 Arguments* thisObject = jsCast<Arguments*>(cell); 244 if (i < thisObject-> d->numArguments) {240 if (i < thisObject->m_numArguments) { 245 241 if (!Base::deletePropertyByIndex(cell, exec, i)) 246 242 return false; 247 248 if (!thisObject->d->deletedArguments) { 249 thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); 250 memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); 251 } 252 if (!thisObject->d->deletedArguments[i]) { 253 thisObject->d->deletedArguments[i] = true; 243 if (thisObject->tryDeleteArgument(i)) 254 244 return true; 255 } 256 } 257 245 } 258 246 return JSObject::deletePropertyByIndex(thisObject, exec, i); 259 247 } … … 266 254 Arguments* thisObject = jsCast<Arguments*>(cell); 267 255 unsigned i = propertyName.asIndex(); 268 if (i < thisObject-> d->numArguments) {256 if (i < thisObject->m_numArguments) { 269 257 ASSERT(i < PropertyName::NotAnIndex); 270 258 if (!Base::deleteProperty(cell, exec, propertyName)) 271 259 return false; 272 273 if (!thisObject->d->deletedArguments) { 274 thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); 275 memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); 276 } 277 if (!thisObject->d->deletedArguments[i]) { 278 thisObject->d->deletedArguments[i] = true; 260 if (thisObject->tryDeleteArgument(i)) 279 261 return true; 280 } 281 } 282 283 if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { 284 thisObject->d->overrodeLength = true; 285 return true; 286 } 287 288 if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { 289 if (!thisObject->d->isStrictMode) { 290 thisObject->d->overrodeCallee = true; 262 } 263 264 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 265 thisObject->m_overrodeLength = true; 266 return true; 267 } 268 269 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 270 if (!thisObject->m_isStrictMode) { 271 thisObject->m_overrodeCallee = true; 291 272 return true; 292 273 } … … 294 275 } 295 276 296 if (propertyName == exec->propertyNames().caller && thisObject-> d->isStrictMode)277 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 297 278 thisObject->createStrictModeCallerIfNecessary(exec); 298 279 … … 304 285 Arguments* thisObject = jsCast<Arguments*>(object); 305 286 unsigned i = propertyName.asIndex(); 306 if (i < thisObject-> d->numArguments) {287 if (i < thisObject->m_numArguments) { 307 288 ASSERT(i < PropertyName::NotAnIndex); 308 289 // If the property is not yet present on the object, and is not yet marked as deleted, then add it now. 309 290 PropertySlot slot; 310 if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) 311 object->putDirectMayBeIndex(exec, propertyName, thisObject->argument(i).get()); 291 if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) { 292 JSValue value = thisObject->tryGetArgument(i); 293 ASSERT(value); 294 object->putDirectMayBeIndex(exec, propertyName, value); 295 } 312 296 if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow)) 313 297 return false; 314 298 315 if (!thisObject->d->deletedArguments) {316 thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]);317 memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments);318 }319 299 // From ES 5.1, 10.6 Arguments Object 320 300 // 5. If the value of isMapped is not undefined, then 321 if ( !thisObject->d->deletedArguments[i]) {301 if (thisObject->isArgument(i)) { 322 302 // a. If IsAccessorDescriptor(Desc) is true, then 323 303 if (descriptor.isAccessorDescriptor()) { 324 304 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments. 325 thisObject-> d->deletedArguments[i] = true;305 thisObject->tryDeleteArgument(i); 326 306 } else { // b. Else 327 307 // i. If Desc.[[Value]] is present, then 328 308 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments. 329 309 if (descriptor.value()) 330 thisObject-> argument(i).set(exec->globalData(), thisObject, descriptor.value());310 thisObject->trySetArgument(exec->globalData(), i, descriptor.value()); 331 311 // ii. If Desc.[[Writable]] is present and its value is false, then 332 312 // 1. Call the [[Delete]] internal method of map passing P and false as arguments. 333 313 if (descriptor.writablePresent() && !descriptor.writable()) 334 thisObject-> d->deletedArguments[i] = true;314 thisObject->tryDeleteArgument(i); 335 315 } 336 316 } … … 338 318 } 339 319 340 if (propertyName == exec->propertyNames().length && !thisObject-> d->overrodeLength) {341 thisObject->putDirect(exec->globalData(), propertyName, jsNumber(thisObject-> d->numArguments), DontEnum);342 thisObject-> d->overrodeLength = true;343 } else if (propertyName == exec->propertyNames().callee && !thisObject-> d->overrodeCallee) {344 thisObject->putDirect(exec->globalData(), propertyName, thisObject-> d->callee.get(), DontEnum);345 thisObject-> d->overrodeCallee = true;346 } else if (propertyName == exec->propertyNames().caller && thisObject-> d->isStrictMode)320 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 321 thisObject->putDirect(exec->globalData(), propertyName, jsNumber(thisObject->m_numArguments), DontEnum); 322 thisObject->m_overrodeLength = true; 323 } else if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 324 thisObject->putDirect(exec->globalData(), propertyName, thisObject->m_callee.get(), DontEnum); 325 thisObject->m_overrodeCallee = true; 326 } else if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 347 327 thisObject->createStrictModeCallerIfNecessary(exec); 348 328 … … 355 335 return; 356 336 357 if (! d->numArguments)337 if (!m_numArguments) 358 338 return; 359 339 360 340 // Must be called for the same call frame from which it was created. 361 ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == d->registers);362 363 d->registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[d->numArguments]);364 d->registers = d->registerArray.get() + CallFrame::offsetFor(d->numArguments + 1);341 ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers); 342 343 m_registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[m_numArguments]); 344 m_registers = m_registerArray.get() + CallFrame::offsetFor(m_numArguments + 1); 365 345 366 346 if (!callFrame->isInlineCallFrame()) { 367 for (size_t i = 0; i < d->numArguments; ++i)368 argument(i).set(callFrame->globalData(), this, callFrame->argument(i));347 for (size_t i = 0; i < m_numArguments; ++i) 348 trySetArgument(callFrame->globalData(), i, callFrame->argumentAfterCapture(i)); 369 349 return; 370 350 } … … 374 354 } 375 355 356 void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation) 357 { 358 ASSERT(activation); 359 if (isTornOff()) 360 return; 361 362 if (!m_numArguments) 363 return; 364 365 tearOff(exec); 366 367 SharedSymbolTable* symbolTable = activation->symbolTable(); 368 const SlowArgument* slowArguments = symbolTable->slowArguments(); 369 if (!slowArguments) 370 return; 371 372 ASSERT(symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings); 373 m_activation.set(exec->globalData(), this, activation); 374 375 allocateSlowArguments(); 376 size_t count = min<unsigned>(m_numArguments, symbolTable->parameterCount()); 377 for (size_t i = 0; i < count; ++i) 378 m_slowArguments[i] = slowArguments[i]; 379 } 380 376 381 void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) 377 382 { … … 379 384 return; 380 385 381 if (! d->numArguments)382 return; 383 384 d->registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[d->numArguments]);385 d->registers = d->registerArray.get() + CallFrame::offsetFor(d->numArguments + 1);386 if (!m_numArguments) 387 return; 388 389 m_registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[m_numArguments]); 390 m_registers = m_registerArray.get() + CallFrame::offsetFor(m_numArguments + 1); 386 391 387 392 tearOffForInlineCallFrame( … … 392 397 void Arguments::tearOffForInlineCallFrame(JSGlobalData& globalData, Register* registers, InlineCallFrame* inlineCallFrame) 393 398 { 394 for (size_t i = 0; i < d->numArguments; ++i) {399 for (size_t i = 0; i < m_numArguments; ++i) { 395 400 ValueRecovery& recovery = inlineCallFrame->arguments[i + 1]; 396 401 // In the future we'll support displaced recoveries (indicating that the … … 428 433 break; 429 434 } 430 argument(i).set(globalData, this, value);435 trySetArgument(globalData, i, value); 431 436 } 432 437 } -
trunk/Source/JavaScriptCore/runtime/Arguments.h
r128813 r128832 35 35 namespace JSC { 36 36 37 struct ArgumentsData {38 WTF_MAKE_NONCOPYABLE(ArgumentsData); WTF_MAKE_FAST_ALLOCATED;39 public:40 ArgumentsData() { }41 WriteBarrier<JSActivation> activation;42 43 unsigned numArguments;44 45 // We make these full byte booleans to make them easy to test from the JIT,46 // and because even if they were single-bit booleans we still wouldn't save47 // any space.48 bool overrodeLength;49 bool overrodeCallee;50 bool overrodeCaller;51 bool isStrictMode;52 53 WriteBarrierBase<Unknown>* registers;54 OwnArrayPtr<WriteBarrier<Unknown> > registerArray;55 56 OwnArrayPtr<bool> deletedArguments;57 58 WriteBarrier<JSFunction> callee;59 };60 61 37 class Arguments : public JSDestructibleObject { 38 friend class JIT; 39 friend class DFG::SpeculativeJIT; 62 40 public: 63 41 typedef JSDestructibleObject Base; … … 96 74 uint32_t length(ExecState* exec) const 97 75 { 98 if (UNLIKELY( d->overrodeLength))76 if (UNLIKELY(m_overrodeLength)) 99 77 return get(exec, exec->propertyNames().length).toUInt32(exec); 100 return d->numArguments;78 return m_numArguments; 101 79 } 102 80 … … 104 82 void tearOff(CallFrame*); 105 83 void tearOff(CallFrame*, InlineCallFrame*); 106 bool isTornOff() const { return d->registerArray; } 107 void didTearOffActivation(JSGlobalData& globalData, JSActivation* activation) 108 { 109 if (isTornOff()) 110 return; 111 d->activation.set(globalData, this, activation); 112 d->registers = &activation->registerAt(0); 113 } 84 bool isTornOff() const { return m_registerArray; } 85 void didTearOffActivation(ExecState*, JSActivation*); 114 86 115 87 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) … … 118 90 } 119 91 120 static ptrdiff_t offsetOfData() { return OBJECT_OFFSETOF(Arguments, d); }121 122 92 protected: 123 93 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; … … 140 110 void createStrictModeCalleeIfNecessary(ExecState*); 141 111 112 bool isArgument(size_t); 113 bool trySetArgument(JSGlobalData&, size_t argument, JSValue); 114 JSValue tryGetArgument(size_t argument); 115 bool isDeletedArgument(size_t); 116 bool tryDeleteArgument(size_t); 142 117 WriteBarrierBase<Unknown>& argument(size_t); 118 void allocateSlowArguments(); 143 119 144 120 void init(CallFrame*); 145 121 146 OwnPtr<ArgumentsData> d; 122 WriteBarrier<JSActivation> m_activation; 123 124 unsigned m_numArguments; 125 126 // We make these full byte booleans to make them easy to test from the JIT, 127 // and because even if they were single-bit booleans we still wouldn't save 128 // any space. 129 bool m_overrodeLength; 130 bool m_overrodeCallee; 131 bool m_overrodeCaller; 132 bool m_isStrictMode; 133 134 WriteBarrierBase<Unknown>* m_registers; 135 OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; 136 137 OwnArrayPtr<SlowArgument> m_slowArguments; 138 139 WriteBarrier<JSFunction> m_callee; 147 140 }; 148 141 … … 157 150 inline Arguments::Arguments(CallFrame* callFrame) 158 151 : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) 159 , d(adoptPtr(new ArgumentsData))160 152 { 161 153 } … … 163 155 inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) 164 156 : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) 165 , d(adoptPtr(new ArgumentsData)) 166 { 167 } 168 169 inline WriteBarrierBase<Unknown>& Arguments::argument(size_t i) 170 { 171 return d->registers[CallFrame::argumentOffset(i)]; 157 { 158 } 159 160 inline void Arguments::allocateSlowArguments() 161 { 162 if (m_slowArguments) 163 return; 164 m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); 165 } 166 167 inline bool Arguments::tryDeleteArgument(size_t argument) 168 { 169 if (!isArgument(argument)) 170 return false; 171 allocateSlowArguments(); 172 m_slowArguments[argument].status = SlowArgument::Deleted; 173 return true; 174 } 175 176 inline bool Arguments::trySetArgument(JSGlobalData& globalData, size_t argument, JSValue value) 177 { 178 if (!isArgument(argument)) 179 return false; 180 this->argument(argument).set(globalData, this, value); 181 return true; 182 } 183 184 inline JSValue Arguments::tryGetArgument(size_t argument) 185 { 186 if (!isArgument(argument)) 187 return JSValue(); 188 return this->argument(argument).get(); 189 } 190 191 inline bool Arguments::isDeletedArgument(size_t argument) 192 { 193 if (argument >= m_numArguments) 194 return false; 195 if (!m_slowArguments) 196 return false; 197 if (m_slowArguments[argument].status != SlowArgument::Deleted) 198 return false; 199 return true; 200 } 201 202 inline bool Arguments::isArgument(size_t argument) 203 { 204 if (argument >= m_numArguments) 205 return false; 206 if (m_slowArguments && m_slowArguments[argument].status == SlowArgument::Deleted) 207 return false; 208 return true; 209 } 210 211 inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) 212 { 213 ASSERT(isArgument(argument)); 214 if (!m_slowArguments || m_slowArguments[argument].status == SlowArgument::Normal) 215 return m_registers[CallFrame::argumentOffset(argument)]; 216 217 ASSERT(m_slowArguments[argument].status == SlowArgument::Captured); 218 if (!m_activation) 219 return m_registers[m_slowArguments[argument].indexIfCaptured]; 220 221 return m_activation->registerAt(m_slowArguments[argument].indexIfCaptured); 172 222 } 173 223 … … 178 228 179 229 JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); 180 d->numArguments = callFrame->argumentCount();181 d->registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers());182 d->callee.set(callFrame->globalData(), this, callee);183 d->overrodeLength = false;184 d->overrodeCallee = false;185 d->overrodeCaller = false;186 d->isStrictMode = callFrame->codeBlock()->isStrictMode();230 m_numArguments = callFrame->argumentCount(); 231 m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); 232 m_callee.set(callFrame->globalData(), this, callee); 233 m_overrodeLength = false; 234 m_overrodeCallee = false; 235 m_overrodeCaller = false; 236 m_isStrictMode = callFrame->codeBlock()->isStrictMode(); 187 237 188 238 // The bytecode generator omits op_tear_off_activation in cases of no 189 239 // declared parameters, so we need to tear off immediately. 190 if ( d->isStrictMode || !callee->jsExecutable()->parameterCount())240 if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) 191 241 tearOff(callFrame); 192 242 } … … 198 248 199 249 JSFunction* callee = inlineCallFrame->callee.get(); 200 d->numArguments = inlineCallFrame->arguments.size() - 1;201 d->registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset;202 d->callee.set(callFrame->globalData(), this, callee);203 d->overrodeLength = false;204 d->overrodeCallee = false;205 d->overrodeCaller = false;206 d->isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode();250 m_numArguments = inlineCallFrame->arguments.size() - 1; 251 m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; 252 m_callee.set(callFrame->globalData(), this, callee); 253 m_overrodeLength = false; 254 m_overrodeCallee = false; 255 m_overrodeCaller = false; 256 m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); 207 257 208 258 // The bytecode generator omits op_tear_off_activation in cases of no 209 259 // declared parameters, so we need to tear off immediately. 210 if ( d->isStrictMode || !callee->jsExecutable()->parameterCount())260 if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) 211 261 tearOff(callFrame, inlineCallFrame); 212 262 } -
trunk/Source/JavaScriptCore/runtime/JSActivation.h
r128400 r128832 49 49 static JSActivation* create(JSGlobalData& globalData, CallFrame* callFrame, FunctionExecutable* functionExecutable) 50 50 { 51 size_t storageSize = JSActivation::storageSize( callFrame,functionExecutable->symbolTable());51 size_t storageSize = JSActivation::storageSize(functionExecutable->symbolTable()); 52 52 JSActivation* activation = new ( 53 53 NotNull, … … 99 99 100 100 static size_t allocationSize(size_t storageSize); 101 static size_t storageSize( CallFrame*,SharedSymbolTable*);102 static int captureStart( CallFrame*,SharedSymbolTable*);101 static size_t storageSize(SharedSymbolTable*); 102 static int captureStart(SharedSymbolTable*); 103 103 104 104 int registerOffset(); … … 143 143 } 144 144 145 inline int JSActivation::captureStart( CallFrame* callFrame,SharedSymbolTable* symbolTable)145 inline int JSActivation::captureStart(SharedSymbolTable* symbolTable) 146 146 { 147 147 if (symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings) 148 return -CallFrame::offsetFor(s td::max<size_t>(callFrame->argumentCountIncludingThis(), symbolTable->parameterCountIncludingThis()));148 return -CallFrame::offsetFor(symbolTable->parameterCountIncludingThis()); 149 149 return symbolTable->captureStart(); 150 150 } 151 151 152 inline size_t JSActivation::storageSize( CallFrame* callFrame,SharedSymbolTable* symbolTable)153 { 154 return symbolTable->captureEnd() - captureStart( callFrame,symbolTable);152 inline size_t JSActivation::storageSize(SharedSymbolTable* symbolTable) 153 { 154 return symbolTable->captureEnd() - captureStart(symbolTable); 155 155 } 156 156 157 157 inline int JSActivation::registerOffset() 158 158 { 159 return -captureStart( CallFrame::create(reinterpret_cast<Register*>(m_registers)),symbolTable());159 return -captureStart(symbolTable()); 160 160 } 161 161 162 162 inline size_t JSActivation::storageSize() 163 163 { 164 return storageSize( CallFrame::create(reinterpret_cast<Register*>(m_registers)),symbolTable());164 return storageSize(symbolTable()); 165 165 } 166 166 … … 217 217 inline bool JSActivation::isValid(const SymbolTableEntry& entry) 218 218 { 219 if (entry.getIndex() < captureStart( CallFrame::create(reinterpret_cast<Register*>(m_registers)),symbolTable()))219 if (entry.getIndex() < captureStart(symbolTable())) 220 220 return false; 221 221 if (entry.getIndex() >= symbolTable()->captureEnd()) -
trunk/Source/JavaScriptCore/runtime/SymbolTable.h
r128813 r128832 41 41 class WatchpointSet; 42 42 43 struct SlowArgument { 44 enum Status { 45 Normal = 0, 46 Captured = 1, 47 Deleted = 2 48 }; 49 50 SlowArgument() 51 : status(Normal) 52 , indexIfCaptured(0) 53 { 54 } 55 56 Status status; 57 int indexIfCaptured; // If status is 'Captured', indexIfCaptured is our index in the CallFrame. 58 }; 59 43 60 static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); } 44 61 … … 360 377 void setCaptureEnd(int captureEnd) { m_captureEnd = captureEnd; } 361 378 379 int parameterCount() { return m_parameterCountIncludingThis - 1; } 362 380 int parameterCountIncludingThis() { return m_parameterCountIncludingThis; } 363 381 void setParameterCountIncludingThis(int parameterCountIncludingThis) { m_parameterCountIncludingThis = parameterCountIncludingThis; } 382 383 // 0 if we don't capture any arguments; parameterCount() in length if we do. 384 const SlowArgument* slowArguments() { return m_slowArguments.get(); } 385 void setSlowArguments(PassOwnArrayPtr<SlowArgument> slowArguments) { m_slowArguments = slowArguments; } 364 386 365 387 static JS_EXPORTDATA const ClassInfo s_info; … … 382 404 int m_captureStart; 383 405 int m_captureEnd; 406 407 OwnArrayPtr<SlowArgument> m_slowArguments; 384 408 }; 385 409 386 410 HAS_IMMORTAL_STRUCTURE(SharedSymbolTable); 387 411 388 412 } // namespace JSC 389 413
Note: See TracChangeset
for help on using the changeset viewer.