package otto import ( "strings" "math" ) func (self *_runtime) evaluateConditional(node *_conditionalNode) Value { test := self.evaluate(node.Test) testValue := self.GetValue(test) if toBoolean(testValue) { return self.evaluate(node.Consequent) } return self.evaluate(node.Alternate) } func (self *_runtime) evaluateNew(node *_newNode) Value { callee := self.evaluate(node.Callee) calleeValue := self.GetValue(callee) argumentList := []Value{} for _, argumentNode := range node.ArgumentList { argumentList = append(argumentList, self.GetValue(self.evaluate(argumentNode))) } this := UndefinedValue() return calleeValue._object().Construct(this, argumentList) } func (self *_runtime) evaluateArray(node *_arrayNode) Value { valueArray := []Value{} for _, node := range node.nodeList { valueArray = append(valueArray, self.GetValue(self.evaluate(node))) } result := self.newArray(valueArray) return toValue(result) } func (self *_runtime) evaluateObject(node *_objectNode) Value { result := self.newObject() for _, property := range node.propertyList { result.WriteValue(property.Key, self.GetValue(self.evaluate(property.Value)), false) } return toValue(result) } func (self *_runtime) evaluateRegExp(node *_regExpNode) Value { return toValue(self._newRegExp(node.Pattern, node.Flags)) } func (self *_runtime) evaluateUnaryOperation(node *_unaryOperationNode) Value { target := self.evaluate(node.Target) if node.Operator == "typeof" && target._valueType == valueReference && target.reference().GetBase() == nil { return toValue("undefined") } targetValue := self.GetValue(target) switch node.Operator { case "!": if targetValue.toBoolean() { return FalseValue() } return TrueValue() case "~": integerValue := toI32(targetValue) return toValue(^integerValue) case "+": return toValue(targetValue.toFloat()) case "-": value := targetValue.toFloat() if value == 0 { if math.Signbit(value) { return positiveZeroValue() } return negativeZeroValue() } return toValue(-value) case "++=": // Prefix ++ resultValue := toValue(+1 + targetValue.toFloat()) self.PutValue(target.reference(), resultValue) return resultValue case "--=": // Prefix -- resultValue := toValue(-1 + targetValue.toFloat()) self.PutValue(target.reference(), resultValue) return resultValue case "=++": // Postfix ++ resultValue := toValue(+1 + targetValue.toFloat()) self.PutValue(target.reference(), resultValue) return targetValue case "=--": // Postfix -- resultValue := toValue(-1 + targetValue.toFloat()) self.PutValue(target.reference(), resultValue) return targetValue case "void": return UndefinedValue() case "delete": target.reference().Delete() return UndefinedValue() // FIXME This is wrong/incomplete case "typeof": switch targetValue._valueType { case valueUndefined: return toValue("undefined") case valueNull: return toValue("null") case valueBoolean: return toValue("boolean") case valueNumber: return toValue("number") case valueString: return toValue("string") case valueObject: if targetValue._object().Function != nil { return toValue("function") } return toValue("object") default: // ? } } panic(hereBeDragons()) } func (self *_runtime) evaluateMultiply(left float64, right float64) Value { // TODO 11.5.1 return UndefinedValue() } func (self *_runtime) evaluateDivide(left float64, right float64) Value { if math.IsNaN(left) || math.IsNaN(right) { return NaNValue() } if math.IsInf(left, 0) && math.IsInf(right, 0) { return NaNValue() } if left == 0 && right == 0 { return NaNValue() } if math.IsInf(left, 0) { if math.Signbit(left) == math.Signbit(right) { return positiveInfinityValue() } else { return negativeInfinityValue() } } if math.IsInf(right, 0) { if math.Signbit(left) == math.Signbit(right) { return positiveZeroValue() } else { return negativeZeroValue() } } if right == 0 { if math.Signbit(left) == math.Signbit(right) { return positiveInfinityValue() } else { return negativeInfinityValue() } } return toValue(left / right) } func (self *_runtime) evaluateModulo(left float64, right float64) Value { // TODO 11.5.3 return UndefinedValue() } func (self *_runtime) calculateBinaryOperation(operator string, left Value, right Value) Value { leftValue := self.GetValue(left) switch operator { // Additive case "+": leftValue = toPrimitive(leftValue) rightValue := self.GetValue(right) rightValue = toPrimitive(rightValue) if leftValue.IsString() || rightValue.IsString() { return toValue(strings.Join([]string{leftValue.toString(), rightValue.toString()}, "")) } else { return toValue(leftValue.toFloat() + rightValue.toFloat()) } case "-": rightValue := self.GetValue(right) return toValue(leftValue.toFloat() - rightValue.toFloat()) // Multiplicative case "*": rightValue := self.GetValue(right) return toValue(leftValue.toFloat() * rightValue.toFloat()) case "/": rightValue := self.GetValue(right) return self.evaluateDivide(leftValue.toFloat(), rightValue.toFloat()) case "%": rightValue := self.GetValue(right) return toValue(math.Mod(leftValue.toFloat(), rightValue.toFloat())) // Logical case "&&": left := toBoolean(leftValue) if !left { return FalseValue() } return toValue(toBoolean(self.GetValue(right))) case "||": left := toBoolean(leftValue) if left { return TrueValue() } return toValue(toBoolean(self.GetValue(right))) // Bitwise case "&": rightValue := self.GetValue(right) return toValue(toI32(leftValue) & toI32(rightValue)) case "|": rightValue := self.GetValue(right) return toValue(toI32(leftValue) | toI32(rightValue)) case "^": rightValue := self.GetValue(right) return toValue(toI32(leftValue) ^ toI32(rightValue)) // Shift // (Masking of 0x1f is to restrict the shift to a maximum of 31 places) case "<<": rightValue := self.GetValue(right) return toValue(toI32(leftValue) << (toUI32(rightValue) & 0x1f)) case ">>": rightValue := self.GetValue(right) return toValue(toI32(leftValue) >> (toUI32(rightValue) & 0x1f)) case ">>>": rightValue := self.GetValue(right) // Shifting an unsigned integer is a logical shift return toValue(toUI32(leftValue) >> (toUI32(rightValue) & 0x1f)) case "instanceof": rightValue := self.GetValue(right) if !rightValue.IsObject() { panic(newTypeError()) } return toValue(rightValue._object().HasInstance(leftValue)) case "in": rightValue := self.GetValue(right) if !rightValue.IsObject() { panic(newTypeError()) } return toValue(rightValue._object().HasProperty(toString(leftValue))) } panic(hereBeDragons(operator)) } func (self *_runtime) evaluateAssignment(node *_assignmentNode) Value { left := self.evaluate(node.Left) right := self.evaluate(node.Right) rightValue := self.GetValue(right) result := rightValue if node.Operator != "" { result = self.calculateBinaryOperation(node.Operator, left, rightValue) } self.PutValue(left.reference(), result) return result } func valueKindDispatchKey(left _valueType, right _valueType) int { return (int(left) << 2) + int(right) } var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch() func makeEqualDispatch() map[int](func(Value, Value) bool) { key := valueKindDispatchKey return map[int](func(Value, Value) bool){ key(valueNumber, valueObject): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, key(valueString, valueObject): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, key(valueObject, valueNumber): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, key(valueObject, valueString): func(x Value, y Value) bool { return x.toFloat() == y.toFloat() }, } } type _lessThanResult int const ( lessThanFalse _lessThanResult = iota lessThanTrue lessThanUndefined ) func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult { x := UndefinedValue() y := x if leftFirst { x = toNumberPrimitive(left) y = toNumberPrimitive(right) } else { y = toNumberPrimitive(right) x = toNumberPrimitive(left) } result := false if x._valueType != valueString || y._valueType != valueString { x, y := x.toFloat(), y.toFloat() if math.IsNaN(x) || math.IsNaN(y) { return lessThanUndefined } result = x < y } else { x, y := x.toString(), y.toString() result = x < y } if result { return lessThanTrue } return lessThanFalse } var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){ // < map[_lessThanResult]bool{ lessThanFalse: false, lessThanTrue: true, lessThanUndefined: false, }, // > map[_lessThanResult]bool{ lessThanFalse: false, lessThanTrue: true, lessThanUndefined: false, }, // <= map[_lessThanResult]bool{ lessThanFalse: true, lessThanTrue: false, lessThanUndefined: false, }, // >= map[_lessThanResult]bool{ lessThanFalse: true, lessThanTrue: false, lessThanUndefined: false, }, } func (self *_runtime) calculateComparison(comparator string, left Value, right Value) bool { x := self.GetValue(left) y := self.GetValue(right) kindEqualKind := false result := true negate := false switch comparator { case "<": result = lessThanTable[0][calculateLessThan(x, y, true)] case ">": result = lessThanTable[1][calculateLessThan(y, x, false)] case "<=": result = lessThanTable[2][calculateLessThan(y, x, false)] case ">=": result = lessThanTable[3][calculateLessThan(x, y, true)] case "!==": negate = true fallthrough case "===": if x._valueType != y._valueType { result = false } else { kindEqualKind = true } case "!=": negate = true fallthrough case "==": if x._valueType == y._valueType { kindEqualKind = true } else if x._valueType <= valueUndefined && y._valueType <= valueUndefined { result = true } else if x._valueType <= valueUndefined || y._valueType <= valueUndefined { result = false } else if x._valueType <= valueString && y._valueType <= valueString { result = x.toFloat() == y.toFloat() } else if x._valueType == valueBoolean { result = self.calculateComparison("==", toValue(x.toFloat()), y) } else if y._valueType == valueBoolean { result = self.calculateComparison("==", x, toValue(y.toFloat())) } else if x._valueType == valueObject { result = self.calculateComparison("==", toPrimitive(x), y) } else if y._valueType == valueObject { result = self.calculateComparison("==", x, toPrimitive(y)) } else { panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y)) } } if kindEqualKind { switch x._valueType { case valueUndefined, valueNull: result = true case valueNumber: x := x.toFloat() y := y.toFloat() if math.IsNaN(x) || math.IsNaN(y) { result = false } else { result = x == y } case valueString: result = x.toString() == y.toString() case valueBoolean: result = x.toBoolean() == y.toBoolean() case valueObject: result = x._object() == y._object() default: goto ERROR } } if negate { result = !result } return result ERROR: panic(hereBeDragons("%v (%v) %s %v (%v)", x, x._valueType, comparator, y, y._valueType)) } func (self *_runtime) evaluateComparison(node *_comparisonNode) Value { left := self.evaluate(node.Left) right := self.evaluate(node.Right) return toValue(self.calculateComparison(node.Comparator, left, right)) } func (self *_runtime) evaluateBinaryOperation(node *_binaryOperationNode) Value { left := self.evaluate(node.Left) leftValue := self.GetValue(left) switch node.Operator { // Logical case "&&": if !toBoolean(leftValue) { return leftValue } right := self.evaluate(node.Right) return self.GetValue(right) case "||": if toBoolean(leftValue) { return leftValue } right := self.evaluate(node.Right) return self.GetValue(right) } return self.calculateBinaryOperation(node.Operator, left, self.evaluate(node.Right)) } func (self *_runtime) evaluateCall(node *_callNode) Value { callee := self.evaluate(node.Callee) calleeValue := self.GetValue(callee) argumentList := []Value{} for _, argumentNode := range node.ArgumentList { argumentList = append(argumentList, self.GetValue(self.evaluate(argumentNode))) } this := UndefinedValue() calleeReference := callee.reference() if calleeReference != nil { calleeObject := calleeReference.GetBase() if calleeObject != nil { this = toValue(calleeObject) } } if !calleeValue.IsFunction() { panic(newTypeError("%v is not a function", calleeValue)) } return self.Call(calleeValue._object(), this, argumentList) } func (self *_runtime) evaluateFunction(node *_functionNode) Value { return toValue(self.newNodeFunction(node, self.LexicalEnvironment())) } func (self *_runtime) evaluateDotMember(node *_dotMemberNode) Value { target := self.evaluate(node.Target) targetValue := self.GetValue(target) return toValue(newObjectReference(self.toObject(targetValue), node.Member, false, node)) } func (self *_runtime) evaluateBracketMember(node *_bracketMemberNode) Value { target := self.evaluate(node.Target) targetValue := self.GetValue(target) member := self.evaluate(node.Member) memberValue := self.GetValue(member) return toValue(newObjectReference(self.toObject(targetValue), toString(memberValue), false, node)) } func (self *_runtime) evaluateIdentifier(node *_identifierNode) Value { name := node.Value // 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.LexicalEnvironment(), name, false, node) if reference == nil { // Should never get here! panic(hereBeDragons("referenceError == nil: " + name)) } return toValue(reference) } func (self *_runtime) evaluateValue(node *_valueNode) Value { return node.Value } func (self *_runtime) evaluateComma(node *_commaNode) Value { var result Value for _, node := range node.Sequence { result = self.evaluate(node) result = self.GetValue(result) } return result }