123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- package otto
- import (
- "fmt"
- "math"
- goruntime "runtime"
- "github.com/robertkrimen/otto/token"
- )
- func (rt *runtime) cmplEvaluateNodeExpression(node nodeExpression) Value {
- // Allow interpreter interruption
- // If the Interrupt channel is nil, then
- // we avoid runtime.Gosched() overhead (if any)
- // FIXME: Test this
- if rt.otto.Interrupt != nil {
- goruntime.Gosched()
- select {
- case value := <-rt.otto.Interrupt:
- value()
- default:
- }
- }
- switch node := node.(type) {
- case *nodeArrayLiteral:
- return rt.cmplEvaluateNodeArrayLiteral(node)
- case *nodeAssignExpression:
- return rt.cmplEvaluateNodeAssignExpression(node)
- case *nodeBinaryExpression:
- if node.comparison {
- return rt.cmplEvaluateNodeBinaryExpressionComparison(node)
- }
- return rt.cmplEvaluateNodeBinaryExpression(node)
- case *nodeBracketExpression:
- return rt.cmplEvaluateNodeBracketExpression(node)
- case *nodeCallExpression:
- return rt.cmplEvaluateNodeCallExpression(node, nil)
- case *nodeConditionalExpression:
- return rt.cmplEvaluateNodeConditionalExpression(node)
- case *nodeDotExpression:
- return rt.cmplEvaluateNodeDotExpression(node)
- case *nodeFunctionLiteral:
- local := rt.scope.lexical
- if node.name != "" {
- local = rt.newDeclarationStash(local)
- }
- value := objectValue(rt.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(rt, rt.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 rt.cmplEvaluateNodeNewExpression(node)
- case *nodeObjectLiteral:
- return rt.cmplEvaluateNodeObjectLiteral(node)
- case *nodeRegExpLiteral:
- return objectValue(rt.newRegExpDirect(node.pattern, node.flags))
- case *nodeSequenceExpression:
- return rt.cmplEvaluateNodeSequenceExpression(node)
- case *nodeThisExpression:
- return objectValue(rt.scope.this)
- case *nodeUnaryExpression:
- return rt.cmplEvaluateNodeUnaryExpression(node)
- case *nodeVariableExpression:
- return rt.cmplEvaluateNodeVariableExpression(node)
- default:
- panic(fmt.Sprintf("unknown node type: %T", node))
- }
- }
- func (rt *runtime) cmplEvaluateNodeArrayLiteral(node *nodeArrayLiteral) Value {
- valueArray := []Value{}
- for _, node := range node.value {
- if node == nil {
- valueArray = append(valueArray, emptyValue)
- } else {
- valueArray = append(valueArray, rt.cmplEvaluateNodeExpression(node).resolve())
- }
- }
- result := rt.newArrayOf(valueArray)
- return objectValue(result)
- }
- func (rt *runtime) cmplEvaluateNodeAssignExpression(node *nodeAssignExpression) Value {
- left := rt.cmplEvaluateNodeExpression(node.left)
- right := rt.cmplEvaluateNodeExpression(node.right)
- rightValue := right.resolve()
- result := rightValue
- if node.operator != token.ASSIGN {
- result = rt.calculateBinaryExpression(node.operator, left, rightValue)
- }
- rt.putValue(left.reference(), result)
- return result
- }
- func (rt *runtime) cmplEvaluateNodeBinaryExpression(node *nodeBinaryExpression) Value {
- left := rt.cmplEvaluateNodeExpression(node.left)
- leftValue := left.resolve()
- switch node.operator {
- // Logical
- case token.LOGICAL_AND:
- if !leftValue.bool() {
- return leftValue
- }
- right := rt.cmplEvaluateNodeExpression(node.right)
- return right.resolve()
- case token.LOGICAL_OR:
- if leftValue.bool() {
- return leftValue
- }
- right := rt.cmplEvaluateNodeExpression(node.right)
- return right.resolve()
- }
- return rt.calculateBinaryExpression(node.operator, leftValue, rt.cmplEvaluateNodeExpression(node.right))
- }
- func (rt *runtime) cmplEvaluateNodeBinaryExpressionComparison(node *nodeBinaryExpression) Value {
- left := rt.cmplEvaluateNodeExpression(node.left).resolve()
- right := rt.cmplEvaluateNodeExpression(node.right).resolve()
- return boolValue(rt.calculateComparison(node.operator, left, right))
- }
- func (rt *runtime) cmplEvaluateNodeBracketExpression(node *nodeBracketExpression) Value {
- target := rt.cmplEvaluateNodeExpression(node.left)
- targetValue := target.resolve()
- member := rt.cmplEvaluateNodeExpression(node.member)
- memberValue := member.resolve()
- // TODO Pass in base value as-is, and defer toObject till later?
- obj, err := rt.objectCoerce(targetValue)
- if err != nil {
- panic(rt.panicTypeError("Cannot access member %q of %s", memberValue.string(), err, at(node.idx)))
- }
- return toValue(newPropertyReference(rt, obj, memberValue.string(), false, at(node.idx)))
- }
- func (rt *runtime) cmplEvaluateNodeCallExpression(node *nodeCallExpression, withArgumentList []interface{}) Value {
- this := Value{}
- callee := rt.cmplEvaluateNodeExpression(node.callee)
- argumentList := []Value{}
- if withArgumentList != nil {
- argumentList = rt.toValueArray(withArgumentList...)
- } else {
- for _, argumentNode := range node.argumentList {
- argumentList = append(argumentList, rt.cmplEvaluateNodeExpression(argumentNode).resolve())
- }
- }
- eval := false // Whether this call is a (candidate for) direct call to eval
- name := ""
- if rf := callee.reference(); rf != nil {
- switch rf := rf.(type) {
- case *propertyReference:
- name = rf.name
- this = objectValue(rf.base)
- 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("unexpected callee type %T to node call expression", rf))
- }
- }
- atv := at(-1)
- switch callee := node.callee.(type) {
- case *nodeIdentifier:
- atv = at(callee.idx)
- case *nodeDotExpression:
- atv = at(callee.idx)
- case *nodeBracketExpression:
- atv = at(callee.idx)
- }
- frm := frame{
- callee: name,
- file: rt.scope.frame.file,
- }
- vl := callee.resolve()
- if !vl.IsFunction() {
- if name == "" {
- // FIXME Maybe typeof?
- panic(rt.panicTypeError("%v is not a function", vl, atv))
- }
- panic(rt.panicTypeError("%q is not a function", name, atv))
- }
- rt.scope.frame.offset = int(atv)
- return vl.object().call(this, argumentList, eval, frm)
- }
- func (rt *runtime) cmplEvaluateNodeConditionalExpression(node *nodeConditionalExpression) Value {
- test := rt.cmplEvaluateNodeExpression(node.test)
- testValue := test.resolve()
- if testValue.bool() {
- return rt.cmplEvaluateNodeExpression(node.consequent)
- }
- return rt.cmplEvaluateNodeExpression(node.alternate)
- }
- func (rt *runtime) cmplEvaluateNodeDotExpression(node *nodeDotExpression) Value {
- target := rt.cmplEvaluateNodeExpression(node.left)
- targetValue := target.resolve()
- // TODO Pass in base value as-is, and defer toObject till later?
- obj, err := rt.objectCoerce(targetValue)
- if err != nil {
- panic(rt.panicTypeError("Cannot access member %q of %s", node.identifier, err, at(node.idx)))
- }
- return toValue(newPropertyReference(rt, obj, node.identifier, false, at(node.idx)))
- }
- func (rt *runtime) cmplEvaluateNodeNewExpression(node *nodeNewExpression) Value {
- callee := rt.cmplEvaluateNodeExpression(node.callee)
- argumentList := []Value{}
- for _, argumentNode := range node.argumentList {
- argumentList = append(argumentList, rt.cmplEvaluateNodeExpression(argumentNode).resolve())
- }
- var name string
- if rf := callee.reference(); rf != nil {
- switch rf := rf.(type) {
- case *propertyReference:
- name = rf.name
- case *stashReference:
- name = rf.name
- default:
- panic(rt.panicTypeError("node new expression unexpected callee type %T", rf))
- }
- }
- atv := at(-1)
- switch callee := node.callee.(type) {
- case *nodeIdentifier:
- atv = at(callee.idx)
- case *nodeDotExpression:
- atv = at(callee.idx)
- case *nodeBracketExpression:
- atv = at(callee.idx)
- }
- vl := callee.resolve()
- if !vl.IsFunction() {
- if name == "" {
- // FIXME Maybe typeof?
- panic(rt.panicTypeError("%v is not a function", vl, atv))
- }
- panic(rt.panicTypeError("'%s' is not a function", name, atv))
- }
- rt.scope.frame.offset = int(atv)
- return vl.object().construct(argumentList)
- }
- func (rt *runtime) cmplEvaluateNodeObjectLiteral(node *nodeObjectLiteral) Value {
- result := rt.newObject()
- for _, prop := range node.value {
- switch prop.kind {
- case "value":
- result.defineProperty(prop.key, rt.cmplEvaluateNodeExpression(prop.value).resolve(), 0o111, false)
- case "get":
- getter := rt.newNodeFunction(prop.value.(*nodeFunctionLiteral), rt.scope.lexical)
- descriptor := property{}
- descriptor.mode = 0o211
- descriptor.value = propertyGetSet{getter, nil}
- result.defineOwnProperty(prop.key, descriptor, false)
- case "set":
- setter := rt.newNodeFunction(prop.value.(*nodeFunctionLiteral), rt.scope.lexical)
- descriptor := property{}
- descriptor.mode = 0o211
- descriptor.value = propertyGetSet{nil, setter}
- result.defineOwnProperty(prop.key, descriptor, false)
- default:
- panic(fmt.Sprintf("unknown node object literal property kind %T", prop.kind))
- }
- }
- return objectValue(result)
- }
- func (rt *runtime) cmplEvaluateNodeSequenceExpression(node *nodeSequenceExpression) Value {
- var result Value
- for _, node := range node.sequence {
- result = rt.cmplEvaluateNodeExpression(node)
- result = result.resolve()
- }
- return result
- }
- func (rt *runtime) cmplEvaluateNodeUnaryExpression(node *nodeUnaryExpression) Value {
- target := rt.cmplEvaluateNodeExpression(node.operand)
- switch node.operator {
- case token.TYPEOF, token.DELETE:
- if target.kind == valueReference && target.reference().invalid() {
- if node.operator == token.TYPEOF {
- return stringValue("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 int32Value(^integerValue)
- case token.PLUS:
- targetValue := target.resolve()
- return float64Value(targetValue.float64())
- case token.MINUS:
- targetValue := target.resolve()
- value := targetValue.float64()
- // TODO Test this
- sign := float64(-1)
- if math.Signbit(value) {
- sign = 1
- }
- return float64Value(math.Copysign(value, sign))
- case token.INCREMENT:
- targetValue := target.resolve()
- if node.postfix {
- // Postfix++
- oldValue := targetValue.float64()
- newValue := float64Value(+1 + oldValue)
- rt.putValue(target.reference(), newValue)
- return float64Value(oldValue)
- }
- // ++Prefix
- newValue := float64Value(+1 + targetValue.float64())
- rt.putValue(target.reference(), newValue)
- return newValue
- case token.DECREMENT:
- targetValue := target.resolve()
- if node.postfix {
- // Postfix--
- oldValue := targetValue.float64()
- newValue := float64Value(-1 + oldValue)
- rt.putValue(target.reference(), newValue)
- return float64Value(oldValue)
- }
- // --Prefix
- newValue := float64Value(-1 + targetValue.float64())
- rt.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 boolValue(target.reference().delete())
- case token.TYPEOF:
- targetValue := target.resolve()
- switch targetValue.kind {
- case valueUndefined:
- return stringValue("undefined")
- case valueNull:
- return stringValue("object")
- case valueBoolean:
- return stringValue("boolean")
- case valueNumber:
- return stringValue("number")
- case valueString:
- return stringValue("string")
- case valueObject:
- if targetValue.object().isCall() {
- return stringValue("function")
- }
- return stringValue("object")
- default:
- // FIXME ?
- }
- }
- panic(hereBeDragons())
- }
- func (rt *runtime) cmplEvaluateNodeVariableExpression(node *nodeVariableExpression) Value {
- if node.initializer != nil {
- // FIXME If reference is nil
- left := getIdentifierReference(rt, rt.scope.lexical, node.name, false, at(node.idx))
- right := rt.cmplEvaluateNodeExpression(node.initializer)
- rightValue := right.resolve()
- rt.putValue(left, rightValue)
- }
- return stringValue(node.name)
- }
|