289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
461 lines
13 KiB
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)
|
|
}
|