Browse Source

Handle nil values in FunctionCall (fixes #20).

Dmitry Panov 8 years ago
parent
commit
8fa3e12080
4 changed files with 67 additions and 8 deletions
  1. 1 5
      builtin_function.go
  2. 17 3
      func.go
  3. 24 0
      runtime_test.go
  4. 25 0
      vm_test.go

+ 1 - 5
builtin_function.go

@@ -45,11 +45,7 @@ func (r *Runtime) toValueArray(a Value) []Value {
 	l := toUInt32(obj.self.getStr("length"))
 	ret := make([]Value, l)
 	for i := uint32(0); i < l; i++ {
-		o := obj.self.get(valueInt(i))
-		if o == nil {
-			o = _undefined
-		}
-		ret[i] = o
+		ret[i] = obj.self.get(valueInt(i))
 	}
 	return ret
 }

+ 17 - 3
func.go

@@ -102,11 +102,25 @@ func (f *funcObject) construct(args []Value) *Object {
 func (f *funcObject) Call(call FunctionCall) Value {
 	vm := f.val.runtime.vm
 	pc := vm.pc
-	vm.push(f.val)
-	vm.push(call.This)
+
+	vm.stack.expand(vm.sp + len(call.Arguments) + 1)
+	vm.stack[vm.sp] = f.val
+	vm.sp++
+	if call.This != nil {
+		vm.stack[vm.sp] = call.This
+	} else {
+		vm.stack[vm.sp] = _undefined
+	}
+	vm.sp++
 	for _, arg := range call.Arguments {
-		vm.push(arg)
+		if arg != nil {
+			vm.stack[vm.sp] = arg
+		} else {
+			vm.stack[vm.sp] = _undefined
+		}
+		vm.sp++
 	}
+
 	vm.pc = -1
 	vm.pushCtx()
 	vm.args = len(call.Arguments)

+ 24 - 0
runtime_test.go

@@ -735,6 +735,30 @@ func TestNilApplyArg(t *testing.T) {
 	testScript1(SCRIPT, valueTrue, t)
 }
 
+func TestNilCallArg(t *testing.T) {
+	const SCRIPT = `
+	"use strict";
+	function f(a) {
+		return this === undefined && a === undefined;
+	}
+	`
+	vm := New()
+	prg, err := Compile("test.js", SCRIPT, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	vm.RunProgram(prg)
+	if f, ok := AssertFunction(vm.Get("f")); ok {
+		v, err := f(nil, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !v.StrictEquals(valueTrue) {
+			t.Fatalf("Unexpected result: %v", v)
+		}
+	}
+}
+
 /*
 func TestArrayConcatSparse(t *testing.T) {
 function foo(a,b,c)

+ 25 - 0
vm_test.go

@@ -357,3 +357,28 @@ func BenchmarkVMAdd(b *testing.B) {
 		vm.sp++
 	}
 }
+
+func BenchmarkFuncCall(b *testing.B) {
+	const SCRIPT = `
+	function f(a, b, c, d) {
+	}
+	`
+
+	b.StopTimer()
+
+	vm := New()
+	prg, err := Compile("test.js", SCRIPT, false)
+	if err != nil {
+		b.Fatal(err)
+	}
+
+	vm.RunProgram(prg)
+	if f, ok := AssertFunction(vm.Get("f")); ok {
+		b.StartTimer()
+		for i := 0; i < b.N; i++ {
+			f(nil, nil, intToValue(1), intToValue(2), intToValue(3), intToValue(4), intToValue(5), intToValue(6))
+		}
+	} else {
+		b.Fatal("f is not a function")
+	}
+}