1package otto
2
3import (
4	"fmt"
5	"regexp"
6	"strings"
7	"unicode"
8
9	"github.com/robertkrimen/otto/parser"
10)
11
12// Function
13
14func builtinFunction(call FunctionCall) Value {
15	return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList))
16}
17
18func builtinNewFunction(self *_object, argumentList []Value) Value {
19	return toValue_object(builtinNewFunctionNative(self.runtime, argumentList))
20}
21
22func argumentList2parameterList(argumentList []Value) []string {
23	parameterList := make([]string, 0, len(argumentList))
24	for _, value := range argumentList {
25		tmp := strings.FieldsFunc(value.string(), func(chr rune) bool {
26			return chr == ',' || unicode.IsSpace(chr)
27		})
28		parameterList = append(parameterList, tmp...)
29	}
30	return parameterList
31}
32
33var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
34
35func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object {
36	var parameterList, body string
37	count := len(argumentList)
38	if count > 0 {
39		tmp := make([]string, 0, count-1)
40		for _, value := range argumentList[0 : count-1] {
41			tmp = append(tmp, value.string())
42		}
43		parameterList = strings.Join(tmp, ",")
44		body = argumentList[count-1].string()
45	}
46
47	// FIXME
48	function, err := parser.ParseFunction(parameterList, body)
49	runtime.parseThrow(err) // Will panic/throw appropriately
50	cmpl := _compiler{}
51	cmpl_function := cmpl.parseExpression(function)
52
53	return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash)
54}
55
56func builtinFunction_toString(call FunctionCall) Value {
57	object := call.thisClassObject("Function") // Should throw a TypeError unless Function
58	switch fn := object.value.(type) {
59	case _nativeFunctionObject:
60		return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name))
61	case _nodeFunctionObject:
62		return toValue_string(fn.node.source)
63	case _bindFunctionObject:
64		return toValue_string("function () { [native code] }")
65	}
66
67	panic(call.runtime.panicTypeError("Function.toString()"))
68}
69
70func builtinFunction_apply(call FunctionCall) Value {
71	if !call.This.isCallable() {
72		panic(call.runtime.panicTypeError())
73	}
74	this := call.Argument(0)
75	if this.IsUndefined() {
76		// FIXME Not ECMA5
77		this = toValue_object(call.runtime.globalObject)
78	}
79	argumentList := call.Argument(1)
80	switch argumentList.kind {
81	case valueUndefined, valueNull:
82		return call.thisObject().call(this, nil, false, nativeFrame)
83	case valueObject:
84	default:
85		panic(call.runtime.panicTypeError())
86	}
87
88	arrayObject := argumentList._object()
89	thisObject := call.thisObject()
90	length := int64(toUint32(arrayObject.get("length")))
91	valueArray := make([]Value, length)
92	for index := int64(0); index < length; index++ {
93		valueArray[index] = arrayObject.get(arrayIndexToString(index))
94	}
95	return thisObject.call(this, valueArray, false, nativeFrame)
96}
97
98func builtinFunction_call(call FunctionCall) Value {
99	if !call.This.isCallable() {
100		panic(call.runtime.panicTypeError())
101	}
102	thisObject := call.thisObject()
103	this := call.Argument(0)
104	if this.IsUndefined() {
105		// FIXME Not ECMA5
106		this = toValue_object(call.runtime.globalObject)
107	}
108	if len(call.ArgumentList) >= 1 {
109		return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame)
110	}
111	return thisObject.call(this, nil, false, nativeFrame)
112}
113
114func builtinFunction_bind(call FunctionCall) Value {
115	target := call.This
116	if !target.isCallable() {
117		panic(call.runtime.panicTypeError())
118	}
119	targetObject := target._object()
120
121	this := call.Argument(0)
122	argumentList := call.slice(1)
123	if this.IsUndefined() {
124		// FIXME Do this elsewhere?
125		this = toValue_object(call.runtime.globalObject)
126	}
127
128	return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList))
129}
130