Commit 5458eac1 authored by kasperl@chromium.org's avatar kasperl@chromium.org

Improve performance of arguments object allocation by taking

care of arguments adaptor frames in the generated code.
Review URL: http://codereview.chromium.org/6262

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@434 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 192d439f
...@@ -587,9 +587,15 @@ void ArmCodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -587,9 +587,15 @@ void ArmCodeGenerator::GenCode(FunctionLiteral* fun) {
Comment cmnt(masm_, "[ allocate arguments object"); Comment cmnt(masm_, "[ allocate arguments object");
{ Reference shadow_ref(this, scope->arguments_shadow()); { Reference shadow_ref(this, scope->arguments_shadow());
{ Reference arguments_ref(this, scope->arguments()); { Reference arguments_ref(this, scope->arguments());
__ ldr(r0, FunctionOperand()); ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
__ push(r0); __ ldr(r2, FunctionOperand());
__ CallRuntime(Runtime::kNewArguments, 1); // The receiver is below the arguments, the return address,
// and the frame pointer on the stack.
const int kReceiverDisplacement = 2 + scope->num_parameters();
__ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
__ mov(r0, Operand(Smi::FromInt(scope->num_parameters())));
__ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
__ CallStub(&stub);
__ push(r0); __ push(r0);
SetValue(&arguments_ref); SetValue(&arguments_ref);
} }
...@@ -963,28 +969,6 @@ class InvokeBuiltinStub : public CodeStub { ...@@ -963,28 +969,6 @@ class InvokeBuiltinStub : public CodeStub {
}; };
class ArgumentsAccessStub: public CodeStub {
public:
explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { }
private:
bool is_length_;
Major MajorKey() { return ArgumentsAccess; }
int MinorKey() { return is_length_ ? 1 : 0; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "ArgumentsAccessStub"; }
#ifdef DEBUG
void Print() {
PrintF("ArgumentsAccessStub (is_length %s)\n",
is_length_ ? "true" : "false");
}
#endif
};
void ArmCodeGenerator::GetReferenceProperty(Expression* key) { void ArmCodeGenerator::GetReferenceProperty(Expression* key) {
ASSERT(!ref()->is_illegal()); ASSERT(!ref()->is_illegal());
Reference::Type type = ref()->type(); Reference::Type type = ref()->type();
...@@ -2857,7 +2841,7 @@ void ArmCodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { ...@@ -2857,7 +2841,7 @@ void ArmCodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
__ mov(r0, Operand(Smi::FromInt(scope_->num_parameters()))); __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
// Call the shared stub to get to the arguments.length. // Call the shared stub to get to the arguments.length.
ArgumentsAccessStub stub(true); ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
__ CallStub(&stub); __ CallStub(&stub);
__ push(r0); __ push(r0);
} }
...@@ -2873,7 +2857,7 @@ void ArmCodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { ...@@ -2873,7 +2857,7 @@ void ArmCodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
__ mov(r0, Operand(Smi::FromInt(scope_->num_parameters()))); __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
// Call the shared stub to get to arguments[key]. // Call the shared stub to get to arguments[key].
ArgumentsAccessStub stub(false); ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
__ CallStub(&stub); __ CallStub(&stub);
__ push(r0); __ push(r0);
} }
...@@ -4426,9 +4410,9 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4426,9 +4410,9 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
// -- lr: return address // -- lr: return address
// ----------------------------------- // -----------------------------------
// Check that the key is a smi for non-length accesses. // If we're reading an element we need to check that the key is a smi.
Label slow; Label slow;
if (!is_length_) { if (type_ == READ_ELEMENT) {
__ tst(r1, Operand(kSmiTagMask)); __ tst(r1, Operand(kSmiTagMask));
__ b(ne, &slow); __ b(ne, &slow);
} }
...@@ -4440,15 +4424,20 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4440,15 +4424,20 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
__ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
__ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL)); __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL));
__ b(eq, &adaptor); if (type_ == NEW_OBJECT) {
__ b(ne, &slow);
} else {
__ b(eq, &adaptor);
}
static const int kParamDisplacement = static const int kParamDisplacement =
StandardFrameConstants::kCallerSPOffset - kPointerSize; StandardFrameConstants::kCallerSPOffset - kPointerSize;
if (is_length_) { if (type_ == READ_LENGTH) {
// Nothing to do: the formal length of parameters has been passed in r0 // Nothing to do: The formal number of parameters has already been
// by the calling function. // passed in register r0 by calling function. Just return it.
} else { __ mov(pc, lr);
} else if (type_ == READ_ELEMENT) {
// Check index against formal parameter count. Use unsigned comparison to // Check index against formal parameter count. Use unsigned comparison to
// get the negative check for free. // get the negative check for free.
// r0: formal number of parameters // r0: formal number of parameters
...@@ -4456,15 +4445,16 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4456,15 +4445,16 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
__ cmp(r1, r0); __ cmp(r1, r0);
__ b(cs, &slow); __ b(cs, &slow);
// Read the argument from the current frame. // Read the argument from the current frame and return it.
__ sub(r3, r0, r1); __ sub(r3, r0, r1);
__ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
__ ldr(r0, MemOperand(r3, kParamDisplacement)); __ ldr(r0, MemOperand(r3, kParamDisplacement));
__ mov(pc, lr);
} else {
ASSERT(type_ == NEW_OBJECT);
// Do nothing here.
} }
// Return to the calling function.
__ mov(pc, lr);
// An arguments adaptor frame is present. Find the length or the actual // An arguments adaptor frame is present. Find the length or the actual
// argument in the calling frame. // argument in the calling frame.
// r0: formal number of parameters // r0: formal number of parameters
...@@ -4475,7 +4465,10 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4475,7 +4465,10 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
// only accessing the length, otherwise it is used in accessing the value // only accessing the length, otherwise it is used in accessing the value
__ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
if (!is_length_) { if (type_ == READ_LENGTH) {
// Return the length in r0.
__ mov(pc, lr);
} else if (type_ == READ_ELEMENT) {
// Check index against actual arguments count. Use unsigned comparison to // Check index against actual arguments count. Use unsigned comparison to
// get the negative check for free. // get the negative check for free.
// r0: actual number of parameter // r0: actual number of parameter
...@@ -4484,16 +4477,24 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4484,16 +4477,24 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
__ cmp(r1, r0); __ cmp(r1, r0);
__ b(cs, &slow); __ b(cs, &slow);
// Read the argument from the adaptor frame. // Read the argument from the adaptor frame and return it.
__ sub(r3, r0, r1); __ sub(r3, r0, r1);
__ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
__ ldr(r0, MemOperand(r3, kParamDisplacement)); __ ldr(r0, MemOperand(r3, kParamDisplacement));
__ mov(pc, lr);
} else {
ASSERT(type_ == NEW_OBJECT);
// Patch the arguments.length and the parameters pointer.
__ str(r0, MemOperand(sp, 0 * kPointerSize));
__ add(r3, r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
__ add(r3, r3, Operand(kParamDisplacement + 1 * kPointerSize));
__ str(r3, MemOperand(sp, 1 * kPointerSize));
__ bind(&slow);
__ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3);
} }
// Return to the calling function. // Return to the calling function.
__ mov(pc, lr); if (type_ == READ_ELEMENT) {
if (!is_length_) {
__ bind(&slow); __ bind(&slow);
__ push(r1); __ push(r1);
__ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
......
...@@ -518,7 +518,6 @@ Ia32CodeGenerator::Ia32CodeGenerator(int buffer_size, ...@@ -518,7 +518,6 @@ Ia32CodeGenerator::Ia32CodeGenerator(int buffer_size,
// edi: caller's parameter pointer // edi: caller's parameter pointer
// esi: callee's context // esi: callee's context
void Ia32CodeGenerator::GenCode(FunctionLiteral* fun) { void Ia32CodeGenerator::GenCode(FunctionLiteral* fun) {
// Record the position for debugging purposes. // Record the position for debugging purposes.
__ RecordPosition(fun->start_position()); __ RecordPosition(fun->start_position());
...@@ -565,8 +564,12 @@ void Ia32CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -565,8 +564,12 @@ void Ia32CodeGenerator::GenCode(FunctionLiteral* fun) {
if (scope->arguments() != NULL) { if (scope->arguments() != NULL) {
ASSERT(scope->arguments_shadow() != NULL); ASSERT(scope->arguments_shadow() != NULL);
Comment cmnt(masm_, "[ allocate arguments object"); Comment cmnt(masm_, "[ allocate arguments object");
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
__ lea(eax, ReceiverOperand());
__ push(FunctionOperand()); __ push(FunctionOperand());
__ CallRuntime(Runtime::kNewArguments, 1); __ push(eax);
__ push(Immediate(Smi::FromInt(scope->num_parameters())));
__ CallStub(&stub);
__ mov(ecx, Operand(eax)); __ mov(ecx, Operand(eax));
arguments_object_allocated = true; arguments_object_allocated = true;
} }
...@@ -1068,28 +1071,6 @@ const char* GenericBinaryOpStub::GetName() { ...@@ -1068,28 +1071,6 @@ const char* GenericBinaryOpStub::GetName() {
} }
class ArgumentsAccessStub: public CodeStub {
public:
explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { }
private:
bool is_length_;
Major MajorKey() { return ArgumentsAccess; }
int MinorKey() { return is_length_ ? 1 : 0; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "ArgumentsAccessStub"; }
#ifdef DEBUG
void Print() {
PrintF("ArgumentsAccessStub (is_length %s)\n",
is_length_ ? "true" : "false");
}
#endif
};
void Ia32CodeGenerator::GenericBinaryOperation(Token::Value op, void Ia32CodeGenerator::GenericBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode) { OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation"); Comment cmnt(masm_, "[ BinaryOperation");
...@@ -3313,7 +3294,7 @@ void Ia32CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { ...@@ -3313,7 +3294,7 @@ void Ia32CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
__ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters()))); __ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters())));
// Call the shared stub to get to the arguments.length. // Call the shared stub to get to the arguments.length.
ArgumentsAccessStub stub(true); ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
__ CallStub(&stub); __ CallStub(&stub);
__ push(eax); __ push(eax);
} }
...@@ -3376,7 +3357,7 @@ void Ia32CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { ...@@ -3376,7 +3357,7 @@ void Ia32CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
__ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters()))); __ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters())));
// Call the shared stub to get to arguments[key]. // Call the shared stub to get to arguments[key].
ArgumentsAccessStub stub(false); ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
__ CallStub(&stub); __ CallStub(&stub);
__ mov(TOS, eax); __ mov(TOS, eax);
} }
...@@ -4815,9 +4796,9 @@ void UnarySubStub::Generate(MacroAssembler* masm) { ...@@ -4815,9 +4796,9 @@ void UnarySubStub::Generate(MacroAssembler* masm) {
void ArgumentsAccessStub::Generate(MacroAssembler* masm) { void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
// Check that the key is a smi for non-length access. // If we're reading an element we need to check that the key is a smi.
Label slow; Label slow;
if (!is_length_) { if (type_ == READ_ELEMENT) {
__ mov(ebx, Operand(esp, 1 * kPointerSize)); // skip return address __ mov(ebx, Operand(esp, 1 * kPointerSize)); // skip return address
__ test(ebx, Immediate(kSmiTagMask)); __ test(ebx, Immediate(kSmiTagMask));
__ j(not_zero, &slow, not_taken); __ j(not_zero, &slow, not_taken);
...@@ -4828,7 +4809,11 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4828,7 +4809,11 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
__ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL); __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
__ j(equal, &adaptor); if (type_ == NEW_OBJECT) {
__ j(not_equal, &slow);
} else {
__ j(equal, &adaptor);
}
// The displacement is used for skipping the return address on the // The displacement is used for skipping the return address on the
// stack. It is the offset of the last parameter (if any) relative // stack. It is the offset of the last parameter (if any) relative
...@@ -4836,31 +4821,35 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4836,31 +4821,35 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
static const int kDisplacement = 1 * kPointerSize; static const int kDisplacement = 1 * kPointerSize;
ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
if (is_length_) { if (type_ == READ_LENGTH) {
// Do nothing. The length is already in register eax. // Nothing to do: The formal number of parameters has already been
} else { // passed in register eax by calling function. Just return it.
__ ret(0);
} else if (type_ == READ_ELEMENT) {
// Check index against formal parameters count limit passed in // Check index against formal parameters count limit passed in
// through register eax. Use unsigned comparison to get negative // through register eax. Use unsigned comparison to get negative
// check for free. // check for free.
__ cmp(ebx, Operand(eax)); __ cmp(ebx, Operand(eax));
__ j(above_equal, &slow, not_taken); __ j(above_equal, &slow, not_taken);
// Read the argument from the stack. // Read the argument from the stack and return it.
__ lea(edx, Operand(ebp, eax, times_2, 0)); __ lea(edx, Operand(ebp, eax, times_2, 0));
__ neg(ebx); __ neg(ebx);
__ mov(eax, Operand(edx, ebx, times_2, kDisplacement)); __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
__ ret(0);
} else {
ASSERT(type_ == NEW_OBJECT);
// Do nothing here.
} }
// Return the length or the argument.
__ ret(0);
// Arguments adaptor case: Find the length or the actual argument in // Arguments adaptor case: Find the length or the actual argument in
// the calling frame. // the calling frame.
__ bind(&adaptor); __ bind(&adaptor);
if (is_length_) { if (type_ == READ_LENGTH) {
// Read the arguments length from the adaptor frame. // Read the arguments length from the adaptor frame and return it.
__ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
} else { __ ret(0);
} else if (type_ == READ_ELEMENT) {
// Check index against actual arguments limit found in the // Check index against actual arguments limit found in the
// arguments adaptor frame. Use unsigned comparison to get // arguments adaptor frame. Use unsigned comparison to get
// negative check for free. // negative check for free.
...@@ -4868,18 +4857,25 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { ...@@ -4868,18 +4857,25 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
__ cmp(ebx, Operand(ecx)); __ cmp(ebx, Operand(ecx));
__ j(above_equal, &slow, not_taken); __ j(above_equal, &slow, not_taken);
// Read the argument from the stack. // Read the argument from the stack and return it.
__ lea(edx, Operand(edx, ecx, times_2, 0)); __ lea(edx, Operand(edx, ecx, times_2, 0));
__ neg(ebx); __ neg(ebx);
__ mov(eax, Operand(edx, ebx, times_2, kDisplacement)); __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
__ ret(0);
} else {
ASSERT(type_ == NEW_OBJECT);
// Patch the arguments.length and the parameters pointer.
__ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ mov(Operand(esp, 1 * kPointerSize), ecx);
__ lea(edx, Operand(edx, ecx, times_2, kDisplacement + 1 * kPointerSize));
__ mov(Operand(esp, 2 * kPointerSize), edx);
__ bind(&slow);
__ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3);
} }
// Return the length or the argument.
__ ret(0);
// Slow-case: Handle non-smi or out-of-bounds access to arguments // Slow-case: Handle non-smi or out-of-bounds access to arguments
// by calling the runtime system. // by calling the runtime system.
if (!is_length_) { if (type_ == READ_ELEMENT) {
__ bind(&slow); __ bind(&slow);
__ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
} }
......
...@@ -342,6 +342,33 @@ class JSConstructEntryStub : public JSEntryStub { ...@@ -342,6 +342,33 @@ class JSConstructEntryStub : public JSEntryStub {
}; };
class ArgumentsAccessStub: public CodeStub {
public:
enum Type {
READ_LENGTH,
READ_ELEMENT,
NEW_OBJECT
};
explicit ArgumentsAccessStub(Type type) : type_(type) { }
private:
Type type_;
Major MajorKey() { return ArgumentsAccess; }
int MinorKey() { return type_; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "ArgumentsAccessStub"; }
#ifdef DEBUG
void Print() {
PrintF("ArgumentsAccessStub (type %d)\n", type_);
}
#endif
};
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -2703,6 +2703,9 @@ static Object* Runtime_Math_tan(Arguments args) { ...@@ -2703,6 +2703,9 @@ static Object* Runtime_Math_tan(Arguments args) {
} }
// The NewArguments function is only used when constructing the
// arguments array when calling non-functions from JavaScript in
// runtime.js:CALL_NON_FUNCTION.
static Object* Runtime_NewArguments(Arguments args) { static Object* Runtime_NewArguments(Arguments args) {
NoHandleAllocation ha; NoHandleAllocation ha;
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
...@@ -2727,6 +2730,26 @@ static Object* Runtime_NewArguments(Arguments args) { ...@@ -2727,6 +2730,26 @@ static Object* Runtime_NewArguments(Arguments args) {
} }
static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
JSFunction* callee = JSFunction::cast(args[0]);
Object** parameters = reinterpret_cast<Object**>(args[1]);
const int length = Smi::cast(args[2])->value();
Object* result = Heap::AllocateArgumentsObject(callee, length);
if (result->IsFailure()) return result;
FixedArray* array = FixedArray::cast(JSObject::cast(result)->elements());
ASSERT(array->length() == length);
FixedArray::WriteBarrierMode mode = array->GetWriteBarrierMode();
for (int i = 0; i < length; i++) {
array->set(i, *--parameters, mode);
}
return result;
}
static Object* Runtime_NewClosure(Arguments args) { static Object* Runtime_NewClosure(Arguments args) {
HandleScope scope; HandleScope scope;
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
......
...@@ -57,6 +57,7 @@ namespace v8 { namespace internal { ...@@ -57,6 +57,7 @@ namespace v8 { namespace internal {
F(GetCalledFunction, 0) \ F(GetCalledFunction, 0) \
F(GetFunctionDelegate, 1) \ F(GetFunctionDelegate, 1) \
F(NewArguments, 1) \ F(NewArguments, 1) \
F(NewArgumentsFast, 3) \
F(LazyCompile, 1) \ F(LazyCompile, 1) \
F(SetNewFunctionAttributes, 1) \ F(SetNewFunctionAttributes, 1) \
\ \
......
...@@ -109,6 +109,7 @@ var knownProblems = { ...@@ -109,6 +109,7 @@ var knownProblems = {
// These functions should not be callable as runtime functions. // These functions should not be callable as runtime functions.
"NewContext": true, "NewContext": true,
"NewArgumentsFast": true,
"PushContext": true, "PushContext": true,
"LazyCompile": true, "LazyCompile": true,
"CreateObjectLiteralBoilerplate": true, "CreateObjectLiteralBoilerplate": true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment