quorum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go

461 lines
13 KiB
Go

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?
object, err := self.objectCoerce(targetValue)
if err != nil {
panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx)))
}
return toValue(newPropertyReference(self, object, 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(), _at(node.idx)))
}
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)
}