diff options
author | Steve Block <steveblock@google.com> | 2010-05-26 10:11:43 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-05-27 11:14:42 +0100 |
commit | e78cbe89e6f337f2f1fe40315be88f742b547151 (patch) | |
tree | d778000b84a04f24bbad50c7fa66244365e960e9 /JavaScriptCore/bytecompiler | |
parent | 7b582e96e4e909ed7dba1e07153d20fbddaec3f7 (diff) | |
download | external_webkit-e78cbe89e6f337f2f1fe40315be88f742b547151.zip external_webkit-e78cbe89e6f337f2f1fe40315be88f742b547151.tar.gz external_webkit-e78cbe89e6f337f2f1fe40315be88f742b547151.tar.bz2 |
Merge WebKit at r60074: Initial merge by git
Change-Id: I18a2dc5439e36c928351ea829d8fb4e39b062fc7
Diffstat (limited to 'JavaScriptCore/bytecompiler')
-rw-r--r-- | JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | 88 | ||||
-rw-r--r-- | JavaScriptCore/bytecompiler/BytecodeGenerator.h | 34 | ||||
-rw-r--r-- | JavaScriptCore/bytecompiler/NodesCodegen.cpp | 34 |
3 files changed, 96 insertions, 60 deletions
diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index e5f3d15..cdf87d2 100644 --- a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -174,8 +174,7 @@ bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, Registe return false; } - ++m_codeBlock->m_numVars; - r0 = newRegister(); + r0 = addVar(); return true; } @@ -295,6 +294,7 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debug , m_symbolTable(symbolTable) , m_scopeNode(functionBody) , m_codeBlock(codeBlock) + , m_activationRegister(0) , m_finallyDepth(0) , m_dynamicScopeDepth(0) , m_baseScopeDepth(0) @@ -312,29 +312,35 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debug codeBlock->setGlobalData(m_globalData); - bool usesArguments = functionBody->usesArguments(); - codeBlock->setUsesArguments(usesArguments); - if (usesArguments) { - m_argumentsRegister.setIndex(RegisterFile::OptionalCalleeArguments); - addVar(propertyNames().arguments, false); - } - if (m_codeBlock->needsFullScopeChain()) { - ++m_codeBlock->m_numVars; - m_activationRegisterIndex = newRegister()->index(); + m_activationRegister = addVar(); emitOpcode(op_enter_with_activation); - instructions().append(m_activationRegisterIndex); + instructions().append(m_activationRegister->index()); } else emitOpcode(op_enter); - if (usesArguments) { + // Both op_tear_off_activation and op_tear_off_arguments tear off the 'arguments' + // object, if created. + if (m_codeBlock->needsFullScopeChain() || functionBody->usesArguments()) { + RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code. + RegisterID* argumentsRegister = addVar(propertyNames().arguments, false); // Can be changed by assigning to 'arguments'. + + // We can save a little space by hard-coding the knowledge that the two + // 'arguments' values are stored in consecutive registers, and storing + // only the index of the assignable one. + codeBlock->setArgumentsRegister(argumentsRegister->index()); + ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->index() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); + emitOpcode(op_init_arguments); + instructions().append(argumentsRegister->index()); // The debugger currently retrieves the arguments object from an activation rather than pulling // it from a call frame. In the long-term it should stop doing that (<rdar://problem/6911886>), // but for now we force eager creation of the arguments object when debugging. - if (m_shouldEmitDebugHooks) + if (m_shouldEmitDebugHooks) { emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } } const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); @@ -359,7 +365,7 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debug ++m_nextParameterIndex; ++m_codeBlock->m_numParameters; - if (functionBody->usesThis() || m_shouldEmitDebugHooks) { + if (!isConstructor() && (functionBody->usesThis() || m_shouldEmitDebugHooks)) { emitOpcode(op_convert_this); instructions().append(m_thisRegister.index()); } @@ -1025,7 +1031,7 @@ bool BytecodeGenerator::findScopedProperty(const Identifier& property, int& inde globalObject = currentVariableObject; return false; } - stackDepth = depth; + stackDepth = depth + m_codeBlock->needsFullScopeChain(); index = entry.getIndex(); if (++iter == end) globalObject = currentVariableObject; @@ -1037,7 +1043,7 @@ bool BytecodeGenerator::findScopedProperty(const Identifier& property, int& inde requiresDynamicChecks |= scopeRequiresDynamicChecks; } // Can't locate the property but we're able to avoid a few lookups. - stackDepth = depth; + stackDepth = depth + m_codeBlock->needsFullScopeChain(); index = missingSymbolMarker(); JSObject* scope = *iter; if (++iter == end) @@ -1385,7 +1391,6 @@ RegisterID* BytecodeGenerator::emitNewRegExp(RegisterID* dst, RegExp* regExp) return dst; } - RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* r0, FuncExprNode* n) { FunctionBodyNode* function = n->body(); @@ -1404,13 +1409,16 @@ RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, Regis void BytecodeGenerator::createArgumentsIfNecessary() { - if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) - emitOpcode(op_create_arguments); + if (m_codeType != FunctionCode) + return; + ASSERT(m_codeBlock->usesArguments()); + + emitOpcode(op_create_arguments); + instructions().append(m_codeBlock->argumentsRegister()); } RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset) { - createArgumentsIfNecessary(); return emitCall(op_call_eval, dst, func, thisRegister, argumentsNode, divot, startOffset, endOffset); } @@ -1466,10 +1474,13 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi // Emit call. emitOpcode(opcodeID); - instructions().append(dst->index()); // dst instructions().append(func->index()); // func instructions().append(argv.size()); // argCount instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); // dst + } if (m_shouldEmitProfileHooks) { emitOpcode(op_profile_did_call); @@ -1511,10 +1522,13 @@ RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func // Emit call. emitOpcode(op_call_varargs); - instructions().append(dst->index()); // dst instructions().append(func->index()); // func instructions().append(argCountRegister->index()); // arg count instructions().append(thisRegister->index() + RegisterFile::CallFrameHeaderSize); // initial registerOffset + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); // dst + } if (m_shouldEmitProfileHooks) { emitOpcode(op_profile_did_call); instructions().append(func->index()); @@ -1526,10 +1540,22 @@ RegisterID* BytecodeGenerator::emitReturn(RegisterID* src) { if (m_codeBlock->needsFullScopeChain()) { emitOpcode(op_tear_off_activation); - instructions().append(m_activationRegisterIndex); - } else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters > 1) + instructions().append(m_activationRegister->index()); + instructions().append(m_codeBlock->argumentsRegister()); + } else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters > 1) { // If there are no named parameters, there's nothing to tear off, since extra / unnamed parameters get copied to the arguments object at construct time. emitOpcode(op_tear_off_arguments); + instructions().append(m_codeBlock->argumentsRegister()); + } + // Constructors use op_ret_object_or_this to check the result is an + // object, unless we can trivially determine the check is not + // necessary (currently, if the return value is 'this'). + if (isConstructor() && (src->index() != m_thisRegister.index())) { + emitOpcode(op_ret_object_or_this); + instructions().append(src->index()); + instructions().append(m_thisRegister.index()); + return src; + } return emitUnaryNoDstOp(op_ret, src); } @@ -1589,16 +1615,15 @@ RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, #endif emitOpcode(op_construct); - instructions().append(dst->index()); // dst instructions().append(func->index()); // func instructions().append(argv.size()); // argCount instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset instructions().append(funcProto->index()); // proto instructions().append(argv[0]->index()); // thisRegister - - emitOpcode(op_construct_verify); - instructions().append(dst->index()); - instructions().append(argv[0]->index()); + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); // dst + } if (m_shouldEmitProfileHooks) { emitOpcode(op_profile_did_call); @@ -1635,7 +1660,6 @@ RegisterID* BytecodeGenerator::emitPushScope(RegisterID* scope) context.isFinallyBlock = false; m_scopeContextStack.append(context); m_dynamicScopeDepth++; - createArgumentsIfNecessary(); return emitUnaryNoDstOp(op_push_scope, scope); } @@ -1899,8 +1923,6 @@ void BytecodeGenerator::emitPushNewScope(RegisterID* dst, const Identifier& prop context.isFinallyBlock = false; m_scopeContextStack.append(context); m_dynamicScopeDepth++; - - createArgumentsIfNecessary(); emitOpcode(op_push_new_scope); instructions().append(dst->index()); diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 0a49392..7626bf4 100644 --- a/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -83,6 +83,8 @@ namespace JSC { JSGlobalData* globalData() const { return m_globalData; } const CommonIdentifiers& propertyNames() const { return *m_globalData->propertyNames; } + bool isConstructor() { return m_codeBlock->m_isConstructor; } + void generate(); // Returns the register corresponding to a local variable, or 0 if no @@ -103,9 +105,6 @@ namespace JSC { // VariableObject that defines the property. If the property cannot be found // statically, depth will contain the depth of the scope chain where dynamic // lookup must begin. - // - // NB: depth does _not_ include the local scope. eg. a depth of 0 refers - // to the scope containing this codeblock. bool findScopedProperty(const Identifier&, int& index, size_t& depth, bool forWriting, bool& includesDynamicScopes, JSObject*& globalObject); // Returns the register storing "this" @@ -150,6 +149,17 @@ namespace JSC { return newTemporary(); } + // Returns the place to write the final output of an operation. + RegisterID* finalDestinationOrIgnored(RegisterID* originalDst, RegisterID* tempDst = 0) + { + if (originalDst) + return originalDst; + ASSERT(tempDst != ignoredResult()); + if (tempDst && tempDst->isTemporary()) + return tempDst; + return newTemporary(); + } + RegisterID* destinationForAssignResult(RegisterID* dst) { if (dst && dst != ignoredResult() && m_codeBlock->needsFullScopeChain()) @@ -404,15 +414,23 @@ namespace JSC { RegisterID* newRegister(); - // Returns the RegisterID corresponding to ident. + // Adds a var slot and maps it to the name ident in symbolTable(). RegisterID* addVar(const Identifier& ident, bool isConstant) { RegisterID* local; addVar(ident, isConstant, local); return local; } - // Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. + + // Ditto. Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. bool addVar(const Identifier&, bool isConstant, RegisterID*&); + + // Adds an anonymous var slot. To give this slot a name, add it to symbolTable(). + RegisterID* addVar() + { + ++m_codeBlock->m_numVars; + return newRegister(); + } // Returns the RegisterID corresponding to ident. RegisterID* addGlobalVar(const Identifier& ident, bool isConstant) @@ -433,9 +451,6 @@ namespace JSC { if (index >= 0) return m_calleeRegisters[index]; - if (index == RegisterFile::OptionalCalleeArguments) - return m_argumentsRegister; - if (m_parameters.size()) { ASSERT(!m_globals.size()); return m_parameters[index + m_parameters.size() + RegisterFile::CallFrameHeaderSize]; @@ -482,8 +497,7 @@ namespace JSC { HashSet<RefPtr<UString::Rep>, IdentifierRepHash> m_functions; RegisterID m_ignoredResultRegister; RegisterID m_thisRegister; - RegisterID m_argumentsRegister; - int m_activationRegisterIndex; + RegisterID* m_activationRegister; SegmentedVector<RegisterID, 32> m_constantPoolRegisters; SegmentedVector<RegisterID, 32> m_calleeRegisters; SegmentedVector<RegisterID, 32> m_parameters; diff --git a/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/JavaScriptCore/bytecompiler/NodesCodegen.cpp index cfd00da..c514b7a 100644 --- a/JavaScriptCore/bytecompiler/NodesCodegen.cpp +++ b/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -316,7 +316,7 @@ RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, Registe RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr<RegisterID> func = generator.emitNode(m_expr); - return generator.emitConstruct(generator.finalDestination(dst), func.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitConstruct(generator.finalDestinationOrIgnored(dst), func.get(), m_args, divot(), startOffset(), endOffset()); } // ------------------------------ EvalFunctionCallNode ---------------------------------- @@ -336,7 +336,7 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re { RefPtr<RegisterID> func = generator.emitNode(m_expr); RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallResolveNode ---------------------------------- @@ -345,7 +345,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, { if (RefPtr<RegisterID> local = generator.registerFor(m_ident)) { RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, thisRegister.get()), local.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, thisRegister.get()), local.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } int index = 0; @@ -355,7 +355,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, if (generator.findScopedProperty(m_ident, index, depth, false, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } RefPtr<RegisterID> func = generator.newTemporary(); @@ -363,7 +363,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, int identifierStart = divot() - startOffset(); generator.emitExpressionInfo(identifierStart + m_ident.size(), m_ident.size(), 0); generator.emitResolveWithBase(thisRegister.get(), func.get(), m_ident); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallBracketNode ---------------------------------- @@ -375,7 +375,7 @@ RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallDotNode ---------------------------------- @@ -388,7 +388,7 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); generator.emitMethodCheck(); generator.emitGetById(function.get(), thisRegister.get(), m_ident); - return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -398,7 +398,7 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RefPtr<RegisterID> base = generator.emitNode(m_base); generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); + RefPtr<RegisterID> finalDestinationOrIgnored = generator.finalDestinationOrIgnored(dst, function.get()); generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); { RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); @@ -410,17 +410,17 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, } else generator.emitLoad(thisRegister.get(), jsNull()); - generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); generator.emitJump(end.get()); m_args->m_listNode = oldList; } generator.emitLabel(realCall.get()); { RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + generator.emitCall(finalDestinationOrIgnored.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } generator.emitLabel(end.get()); - return finalDestination.get(); + return finalDestinationOrIgnored.get(); } static bool areTrivialApplyArguments(ArgumentsNode* args) @@ -441,7 +441,7 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RefPtr<RegisterID> base = generator.emitNode(m_base); generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); + RefPtr<RegisterID> finalDestinationOrIgnored = generator.finalDestinationOrIgnored(dst, function.get()); generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); { if (mayBeCall) { @@ -458,7 +458,7 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, } } else generator.emitLoad(thisRegister.get(), jsNull()); - generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); m_args->m_listNode = oldList; } else { ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); @@ -481,17 +481,17 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, generator.emitNode(args->m_expr); generator.emitLoadVarargs(argsCountRegister.get(), argsRegister.get()); - generator.emitCallVarargs(finalDestination.get(), realFunction.get(), thisRegister.get(), argsCountRegister.get(), divot(), startOffset(), endOffset()); + generator.emitCallVarargs(finalDestinationOrIgnored.get(), realFunction.get(), thisRegister.get(), argsCountRegister.get(), divot(), startOffset(), endOffset()); } generator.emitJump(end.get()); } generator.emitLabel(realCall.get()); { RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + generator.emitCall(finalDestinationOrIgnored.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); } generator.emitLabel(end.get()); - return finalDestination.get(); + return finalDestinationOrIgnored.get(); } // ------------------------------ PostfixResolveNode ---------------------------------- @@ -1979,7 +1979,7 @@ RegisterID* FunctionBodyNode::emitBytecode(BytecodeGenerator& generator, Registe return 0; } - RegisterID* r0 = generator.emitLoad(0, jsUndefined()); + RegisterID* r0 = generator.isConstructor() ? generator.thisRegister() : generator.emitLoad(0, jsUndefined()); generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); generator.emitReturn(r0); return 0; |