diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_evaluate_expression.go')
-rw-r--r-- | Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_evaluate_expression.go | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_evaluate_expression.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_evaluate_expression.go new file mode 100644 index 000000000..34449970e --- /dev/null +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_evaluate_expression.go @@ -0,0 +1,456 @@ +package otto + +import ( + "fmt" + "math" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value { + // Allow interpreter interruption + // If the Interrupt channel is nil, then + // we avoid runtime.Gosched() overhead (if any) + // FIXME: Test this + if self.otto.Interrupt != nil { + runtime.Gosched() + select { + case value := <-self.otto.Interrupt: + value() + default: + } + } + + switch node := node.(type) { + + case *_nodeArrayLiteral: + return self.cmpl_evaluate_nodeArrayLiteral(node) + + case *_nodeAssignExpression: + return self.cmpl_evaluate_nodeAssignExpression(node) + + case *_nodeBinaryExpression: + if node.comparison { + return self.cmpl_evaluate_nodeBinaryExpression_comparison(node) + } else { + return self.cmpl_evaluate_nodeBinaryExpression(node) + } + + case *_nodeBracketExpression: + return self.cmpl_evaluate_nodeBracketExpression(node) + + case *_nodeCallExpression: + return self.cmpl_evaluate_nodeCallExpression(node, nil) + + case *_nodeConditionalExpression: + return self.cmpl_evaluate_nodeConditionalExpression(node) + + case *_nodeDotExpression: + return self.cmpl_evaluate_nodeDotExpression(node) + + case *_nodeFunctionLiteral: + var local = self.scope.lexical + if node.name != "" { + local = self.newDeclarationStash(local) + } + + value := toValue_object(self.newNodeFunction(node, local)) + if node.name != "" { + local.createBinding(node.name, false, value) + } + return value + + case *_nodeIdentifier: + name := node.name + // TODO Should be true or false (strictness) depending on context + // getIdentifierReference should not return nil, but we check anyway and panic + // so as not to propagate the nil into something else + reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx)) + if reference == nil { + // Should never get here! + panic(hereBeDragons("referenceError == nil: " + name)) + } + return toValue(reference) + + case *_nodeLiteral: + return node.value + + case *_nodeNewExpression: + return self.cmpl_evaluate_nodeNewExpression(node) + + case *_nodeObjectLiteral: + return self.cmpl_evaluate_nodeObjectLiteral(node) + + case *_nodeRegExpLiteral: + return toValue_object(self._newRegExp(node.pattern, node.flags)) + + case *_nodeSequenceExpression: + return self.cmpl_evaluate_nodeSequenceExpression(node) + + case *_nodeThisExpression: + return toValue_object(self.scope.this) + + case *_nodeUnaryExpression: + return self.cmpl_evaluate_nodeUnaryExpression(node) + + case *_nodeVariableExpression: + return self.cmpl_evaluate_nodeVariableExpression(node) + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value { + + valueArray := []Value{} + + for _, node := range node.value { + if node == nil { + valueArray = append(valueArray, emptyValue) + } else { + valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve()) + } + } + + result := self.newArrayOf(valueArray) + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + right := self.cmpl_evaluate_nodeExpression(node.right) + rightValue := right.resolve() + + result := rightValue + if node.operator != token.ASSIGN { + result = self.calculateBinaryExpression(node.operator, left, rightValue) + } + + self.putValue(left.reference(), result) + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + leftValue := left.resolve() + + switch node.operator { + // Logical + case token.LOGICAL_AND: + if !leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + case token.LOGICAL_OR: + if leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + } + + return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left).resolve() + right := self.cmpl_evaluate_nodeExpression(node.right).resolve() + + return toValue_bool(self.calculateComparison(node.operator, left, right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + member := self.cmpl_evaluate_nodeExpression(node.member) + memberValue := member.resolve() + + // TODO Pass in base value as-is, and defer toObject till later? + return toValue(newPropertyReference(self, self.toObject(targetValue), memberValue.string(), false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value { + rt := self + this := Value{} + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + if withArgumentList != nil { + argumentList = self.toValueArray(withArgumentList...) + } else { + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + } + + rf := callee.reference() + vl := callee.resolve() + + eval := false // Whether this call is a (candidate for) direct call to eval + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + object := rf.base + this = toValue_object(object) + eval = rf.name == "eval" // Possible direct eval + case *_stashReference: + // TODO ImplicitThisValue + name = rf.name + eval = rf.name == "eval" // Possible direct eval + default: + // FIXME? + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + frame := _frame{ + callee: name, + file: self.scope.frame.file, + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().call(this, argumentList, eval, frame) +} + +func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeExpression(node.consequent) + } + return self.cmpl_evaluate_nodeExpression(node.alternate) +} + +func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error())) + } + return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value { + rt := self + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + + rf := callee.reference() + vl := callee.resolve() + + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + case *_stashReference: + name = rf.name + default: + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().construct(argumentList) +} + +func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value { + + result := self.newObject() + + for _, property := range node.value { + switch property.kind { + case "value": + result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false) + case "get": + getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{getter, nil} + result.defineOwnProperty(property.key, descriptor, false) + case "set": + setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{nil, setter} + result.defineOwnProperty(property.key, descriptor, false) + default: + panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind)) + } + } + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value { + var result Value + for _, node := range node.sequence { + result = self.cmpl_evaluate_nodeExpression(node) + result = result.resolve() + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value { + + target := self.cmpl_evaluate_nodeExpression(node.operand) + switch node.operator { + case token.TYPEOF, token.DELETE: + if target.kind == valueReference && target.reference().invalid() { + if node.operator == token.TYPEOF { + return toValue_string("undefined") + } + return trueValue + } + } + + switch node.operator { + case token.NOT: + targetValue := target.resolve() + if targetValue.bool() { + return falseValue + } + return trueValue + case token.BITWISE_NOT: + targetValue := target.resolve() + integerValue := toInt32(targetValue) + return toValue_int32(^integerValue) + case token.PLUS: + targetValue := target.resolve() + return toValue_float64(targetValue.float64()) + case token.MINUS: + targetValue := target.resolve() + value := targetValue.float64() + // TODO Test this + sign := float64(-1) + if math.Signbit(value) { + sign = 1 + } + return toValue_float64(math.Copysign(value, sign)) + case token.INCREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix++ + oldValue := targetValue.float64() + newValue := toValue_float64(+1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // ++Prefix + newValue := toValue_float64(+1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.DECREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix-- + oldValue := targetValue.float64() + newValue := toValue_float64(-1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // --Prefix + newValue := toValue_float64(-1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.VOID: + target.resolve() // FIXME Side effect? + return Value{} + case token.DELETE: + reference := target.reference() + if reference == nil { + return trueValue + } + return toValue_bool(target.reference().delete()) + case token.TYPEOF: + targetValue := target.resolve() + switch targetValue.kind { + case valueUndefined: + return toValue_string("undefined") + case valueNull: + return toValue_string("object") + case valueBoolean: + return toValue_string("boolean") + case valueNumber: + return toValue_string("number") + case valueString: + return toValue_string("string") + case valueObject: + if targetValue._object().isCall() { + return toValue_string("function") + } + return toValue_string("object") + default: + // FIXME ? + } + } + + panic(hereBeDragons()) +} + +func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value { + if node.initializer != nil { + // FIXME If reference is nil + left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx)) + right := self.cmpl_evaluate_nodeExpression(node.initializer) + rightValue := right.resolve() + + self.putValue(left, rightValue) + } + return toValue_string(node.name) +} |