diff options
Diffstat (limited to 'vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go')
-rw-r--r-- | vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go new file mode 100644 index 000000000..e16c6ac6c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go @@ -0,0 +1,424 @@ +package otto + +import ( + "fmt" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) 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 *_nodeBlockStatement: + labels := self.labels + self.labels = nil + + value := self.cmpl_evaluate_nodeStatementList(node.list) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultBreak: + return emptyValue + } + } + return value + + case *_nodeBranchStatement: + target := node.label + switch node.branch { // FIXME Maybe node.kind? node.operator? + case token.BREAK: + return toValue(newBreakResult(target)) + case token.CONTINUE: + return toValue(newContinueResult(target)) + } + + case *_nodeDebuggerStatement: + if self.debugger != nil { + self.debugger(self.otto) + } + return emptyValue // Nothing happens. + + case *_nodeDoWhileStatement: + return self.cmpl_evaluate_nodeDoWhileStatement(node) + + case *_nodeEmptyStatement: + return emptyValue + + case *_nodeExpressionStatement: + return self.cmpl_evaluate_nodeExpression(node.expression) + + case *_nodeForInStatement: + return self.cmpl_evaluate_nodeForInStatement(node) + + case *_nodeForStatement: + return self.cmpl_evaluate_nodeForStatement(node) + + case *_nodeIfStatement: + return self.cmpl_evaluate_nodeIfStatement(node) + + case *_nodeLabelledStatement: + self.labels = append(self.labels, node.label) + defer func() { + if len(self.labels) > 0 { + self.labels = self.labels[:len(self.labels)-1] // Pop the label + } else { + self.labels = nil + } + }() + return self.cmpl_evaluate_nodeStatement(node.statement) + + case *_nodeReturnStatement: + if node.argument != nil { + return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve())) + } + return toValue(newReturnResult(Value{})) + + case *_nodeSwitchStatement: + return self.cmpl_evaluate_nodeSwitchStatement(node) + + case *_nodeThrowStatement: + value := self.cmpl_evaluate_nodeExpression(node.argument).resolve() + panic(newException(value)) + + case *_nodeTryStatement: + return self.cmpl_evaluate_nodeTryStatement(node) + + case *_nodeVariableStatement: + // Variables are already defined, this is initialization only + for _, variable := range node.list { + self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression)) + } + return emptyValue + + case *_nodeWhileStatement: + return self.cmpl_evaluate_nodeWhileStatement(node) + + case *_nodeWithStatement: + return self.cmpl_evaluate_nodeWithStatement(node) + + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value { + var result Value + for _, node := range list { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + return value + case valueEmpty: + default: + // We have getValue here to (for example) trigger a + // ReferenceError (of the not defined variety) + // Not sure if this is the best way to error out early + // for such errors or if there is a better way + // TODO Do we still need this? + result = value.resolve() + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + test := node.test + + result := emptyValue +resultBreak: + for { + for _, node := range node.body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: do ... while (false) + break + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + source := self.cmpl_evaluate_nodeExpression(node.source) + sourceValue := source.resolve() + + switch sourceValue.kind { + case valueUndefined, valueNull: + return emptyValue + } + + sourceObject := self.toObject(sourceValue) + + into := node.into + body := node.body + + result := emptyValue + object := sourceObject + for object != nil { + enumerateValue := emptyValue + object.enumerate(false, func(name string) bool { + into := self.cmpl_evaluate_nodeExpression(into) + // In the case of: for (var abc in def) ... + if into.reference() == nil { + identifier := into.string() + // TODO Should be true or false (strictness) depending on context + into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1)) + } + self.putValue(into.reference(), toValue_string(name)) + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + enumerateValue = value + return false + case resultBreak: + object = nil + return false + case resultContinue: + return true + } + case valueEmpty: + default: + enumerateValue = value + } + } + return true + }) + if object == nil { + break + } + object = object.prototype + if !enumerateValue.isEmpty() { + result = enumerateValue + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + initializer := node.initializer + test := node.test + update := node.update + body := node.body + + if initializer != nil { + initialResult := self.cmpl_evaluate_nodeExpression(initializer) + initialResult.resolve() // Side-effect trigger + } + + result := emptyValue +resultBreak: + for { + if test != nil { + testResult := self.cmpl_evaluate_nodeExpression(test) + testResultValue := testResult.resolve() + if testResultValue.bool() == false { + break + } + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if update != nil { + updateResult := self.cmpl_evaluate_nodeExpression(update) + updateResult.resolve() // Side-effect trigger + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeStatement(node.consequent) + } else if node.alternate != nil { + return self.cmpl_evaluate_nodeStatement(node.alternate) + } + + return emptyValue +} + +func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant) + target := node.default_ + + for index, clause := range node.body { + test := clause.test + if test != nil { + if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) { + target = index + break + } + } + } + + result := emptyValue + if target != -1 { + for _, clause := range node.body[target:] { + for _, statement := range clause.consequent { + value := self.cmpl_evaluate_nodeStatement(statement) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultReturn: + return value + case resultBreak: + return emptyValue + } + case valueEmpty: + default: + result = value + } + } + } + } + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value { + tryCatchValue, exception := self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.body) + }) + + if exception && node.catch != nil { + outer := self.scope.lexical + self.scope.lexical = self.newDeclarationStash(outer) + defer func() { + self.scope.lexical = outer + }() + // TODO If necessary, convert TypeError<runtime> => TypeError + // That, is, such errors can be thrown despite not being JavaScript "native" + // strict = false + self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false) + + // FIXME node.CatchParameter + // FIXME node.Catch + tryCatchValue, exception = self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.catch.body) + }) + } + + if node.finally != nil { + finallyValue := self.cmpl_evaluate_nodeStatement(node.finally) + if finallyValue.kind == valueResult { + return finallyValue + } + } + + if exception { + panic(newException(tryCatchValue)) + } + + return tryCatchValue +} + +func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value { + + test := node.test + body := node.body + labels := append(self.labels, "") + self.labels = nil + + result := emptyValue +resultBreakContinue: + for { + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: while (false) ... + break + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreakContinue + case resultContinue: + continue resultBreakContinue + } + case valueEmpty: + default: + result = value + } + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value { + object := self.cmpl_evaluate_nodeExpression(node.object) + outer := self.scope.lexical + lexical := self.newObjectStash(self.toObject(object.resolve()), outer) + self.scope.lexical = lexical + defer func() { + self.scope.lexical = outer + }() + + return self.cmpl_evaluate_nodeStatement(node.body) +} |