mirror of
https://github.com/robertkrimen/otto
synced 2025-10-12 20:27:30 +08:00
317 lines
7.2 KiB
Go
317 lines
7.2 KiB
Go
package otto
|
|
|
|
func (self *_runtime) evaluateTryCatch(node *_tryCatchNode) Value {
|
|
tryCatchValue, exception := self.tryCatchEvaluate(func() Value {
|
|
return self.evaluate(node.Try)
|
|
})
|
|
|
|
if exception && node.Catch != nil {
|
|
|
|
lexicalEnvironment := self._executionContext(0).newDeclarativeEnvironment(self)
|
|
defer func() {
|
|
self._executionContext(0).LexicalEnvironment = lexicalEnvironment
|
|
}()
|
|
// TODO If necessary, convert TypeError<runtime> => TypeError
|
|
// That, is, such errors can be thrown despite not being JavaScript "native"
|
|
self.localSet(node.Catch.Identifier, tryCatchValue)
|
|
|
|
tryCatchValue, exception = self.tryCatchEvaluate(func() Value {
|
|
return self.evaluate(node.Catch.Body)
|
|
})
|
|
}
|
|
|
|
if node.Finally != nil {
|
|
finallyValue := self.evaluate(node.Finally)
|
|
if finallyValue.isResult() {
|
|
return finallyValue
|
|
}
|
|
}
|
|
|
|
if exception {
|
|
panic(tryCatchValue)
|
|
}
|
|
|
|
return tryCatchValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateVariableDeclarationList(node *_variableDeclarationListNode) Value {
|
|
for _, node := range node.VariableList {
|
|
self.evaluateVariableDeclaration(node)
|
|
}
|
|
return emptyValue()
|
|
}
|
|
|
|
func (self *_runtime) evaluateVariableDeclaration(node *_variableDeclarationNode) Value {
|
|
if node.Operator != "" {
|
|
// FIXME If reference is nil
|
|
left := getIdentifierReference(self.LexicalEnvironment(), node.Identifier, false, node)
|
|
right := self.evaluate(node.Initializer)
|
|
rightValue := self.GetValue(right)
|
|
|
|
self.PutValue(left, rightValue)
|
|
}
|
|
return toValue_string(node.Identifier)
|
|
}
|
|
|
|
func (self *_runtime) evaluateThrow(node *_throwNode) Value {
|
|
panic(self.GetValue(self.evaluate(node.Argument)))
|
|
}
|
|
|
|
func (self *_runtime) evaluateReturn(node *_returnNode) Value {
|
|
value := UndefinedValue()
|
|
if node.Argument != nil {
|
|
value = self.GetValue(self.evaluate(node.Argument))
|
|
}
|
|
|
|
return toValue(newReturnResult(value))
|
|
}
|
|
|
|
func (self *_runtime) evaluateIf(node *_ifNode) Value {
|
|
test := self.evaluate(node.Test)
|
|
testValue := self.GetValue(test)
|
|
if toBoolean(testValue) {
|
|
return self.evaluate(node.Consequent)
|
|
} else if node.Alternate != nil {
|
|
return self.evaluate(node.Alternate)
|
|
}
|
|
|
|
return emptyValue()
|
|
}
|
|
|
|
func (self *_runtime) evaluateWith(node *_withNode) Value {
|
|
object := self.evaluate(node.Object)
|
|
objectValue := self.GetValue(object)
|
|
previousLexicalEnvironment, lexicalEnvironment := self._executionContext(0).newLexicalEnvironment(self.toObject(objectValue))
|
|
lexicalEnvironment.ProvideThis = true
|
|
defer func() {
|
|
self._executionContext(0).LexicalEnvironment = previousLexicalEnvironment
|
|
}()
|
|
|
|
return self.evaluate(node.Body)
|
|
}
|
|
|
|
func (self *_runtime) evaluateBlock(node *_blockNode) Value {
|
|
|
|
body := node.Body
|
|
labelSet := node.labelSet
|
|
|
|
blockValue := self.evaluateBody(body)
|
|
if blockValue.evaluateBreak(labelSet) == resultBreak {
|
|
return Value{}
|
|
}
|
|
return blockValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateDoWhile(node *_doWhileNode) Value {
|
|
|
|
test := node.Test
|
|
body := node.body
|
|
labelSet := node.labelSet
|
|
|
|
doWhileValue := Value{}
|
|
resultBreak:
|
|
for {
|
|
for _, node := range body {
|
|
value := self.evaluate(node)
|
|
switch value.evaluateBreakContinue(labelSet) {
|
|
case resultReturn:
|
|
return value
|
|
case resultBreak:
|
|
break resultBreak
|
|
case resultContinue:
|
|
goto resultContinue
|
|
default: // resultNormal
|
|
}
|
|
if !value.isEmpty() {
|
|
doWhileValue = value
|
|
}
|
|
}
|
|
resultContinue:
|
|
if !self.GetValue(self.evaluate(test)).isTrue() {
|
|
// Stahp: do ... while (false)
|
|
break
|
|
}
|
|
}
|
|
return doWhileValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateWhile(node *_whileNode) Value {
|
|
|
|
test := node.Test
|
|
body := node.body
|
|
labelSet := node.labelSet
|
|
|
|
whileValue := Value{}
|
|
resultBreakContinue:
|
|
for {
|
|
if !self.GetValue(self.evaluate(test)).isTrue() {
|
|
// Stahp: while (false) ...
|
|
break
|
|
}
|
|
for _, node := range body {
|
|
value := self.evaluate(node)
|
|
switch value.evaluateBreakContinue(labelSet) {
|
|
case resultReturn:
|
|
return value
|
|
case resultBreak:
|
|
break resultBreakContinue
|
|
case resultContinue:
|
|
continue resultBreakContinue
|
|
default: // resultNormal
|
|
}
|
|
if !value.isEmpty() {
|
|
whileValue = value
|
|
}
|
|
}
|
|
}
|
|
return whileValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateFor(node *_forNode) Value {
|
|
|
|
initial := node.Initial
|
|
test := node.Test
|
|
body := node.body
|
|
update := node.Update
|
|
labelSet := node.labelSet
|
|
|
|
if initial != nil {
|
|
initialResult := self.evaluate(initial)
|
|
self.GetValue(initialResult) // Side-effect trigger
|
|
}
|
|
|
|
forValue := Value{}
|
|
resultBreak:
|
|
for {
|
|
if test != nil {
|
|
testResult := self.evaluate(test)
|
|
testResultValue := self.GetValue(testResult)
|
|
if toBoolean(testResultValue) == false {
|
|
break
|
|
}
|
|
}
|
|
if test != nil {
|
|
if !self.GetValue(self.evaluate(test)).isTrue() {
|
|
// Stahp: for (...; false; ...) ...
|
|
break
|
|
}
|
|
}
|
|
for _, node := range body {
|
|
value := self.evaluate(node)
|
|
switch value.evaluateBreakContinue(labelSet) {
|
|
case resultReturn:
|
|
return value
|
|
case resultBreak:
|
|
break resultBreak
|
|
case resultContinue:
|
|
goto resultContinue
|
|
default: // resultNormal
|
|
}
|
|
if !value.isEmpty() {
|
|
forValue = value
|
|
}
|
|
}
|
|
resultContinue:
|
|
if update != nil {
|
|
updateResult := self.evaluate(update)
|
|
self.GetValue(updateResult) // Side-effect trigger
|
|
}
|
|
}
|
|
return forValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateForIn(node *_forInNode) Value {
|
|
|
|
source := self.evaluate(node.Source)
|
|
sourceValue := self.GetValue(source)
|
|
|
|
switch sourceValue._valueType {
|
|
case valueUndefined, valueNull:
|
|
return emptyValue()
|
|
}
|
|
|
|
sourceObject := self.toObject(sourceValue)
|
|
|
|
into := node.Into
|
|
body := node.body
|
|
labelSet := node.labelSet
|
|
|
|
forInValue := Value{}
|
|
object := sourceObject
|
|
for object != nil {
|
|
enumerateValue := Value{}
|
|
object.enumerate(false, func(name string) bool {
|
|
into := self.evaluate(into)
|
|
// In the case of: for (var abc in def) ...
|
|
if into.reference() == nil {
|
|
identifier := toString(into)
|
|
// TODO Should be true or false (strictness) depending on context
|
|
into = toValue(getIdentifierReference(self.LexicalEnvironment(), identifier, false, node))
|
|
}
|
|
self.PutValue(into.reference(), toValue_string(name))
|
|
for _, node := range body {
|
|
value := self.evaluate(node)
|
|
switch value.evaluateBreakContinue(labelSet) {
|
|
case resultReturn:
|
|
enumerateValue = value
|
|
return false
|
|
case resultBreak:
|
|
object = nil
|
|
return false
|
|
case resultContinue:
|
|
return true
|
|
default: // resultNormal
|
|
}
|
|
if !value.isEmpty() {
|
|
enumerateValue = value
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
if object == nil {
|
|
break
|
|
}
|
|
object = object.prototype
|
|
if !enumerateValue.isEmpty() {
|
|
forInValue = enumerateValue
|
|
}
|
|
}
|
|
return forInValue
|
|
}
|
|
|
|
func (self *_runtime) evaluateSwitch(node *_switchNode) Value {
|
|
|
|
discriminantResult := self.evaluate(node.Discriminant)
|
|
target := node.Default
|
|
|
|
for index, clause := range node.CaseList {
|
|
test := clause.Test
|
|
if test != nil {
|
|
if self.calculateComparison("===", discriminantResult, self.evaluate(test)) {
|
|
target = index
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
switchValue := Value{}
|
|
if target != -1 {
|
|
labelSet := node.labelSet
|
|
|
|
for _, clause := range node.CaseList[target:] {
|
|
value := self.evaluateBody(clause.Body)
|
|
switch value.evaluateBreak(labelSet) {
|
|
case resultReturn:
|
|
return value
|
|
case resultBreak:
|
|
return Value{}
|
|
}
|
|
if !value.isEmpty() {
|
|
switchValue = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return switchValue
|
|
}
|