func (ctx *EngineContext) RunInstruction(scope *Scope, ins protoface.Instruction) (returnValue interface{}) { thisFile := ctx.Filename defer func() { //TODO Stack traces seem to get truncated on syslog... if x := recover(); x != nil { err, ok := x.(error) errString := "" if ok { errString = err.Error() } else { errString = x.(string) } if errString != TimeoutError { if ctx.HadError == false { ctx.HadError = true errString = errString + "\n" + constants.Instruction_InstructionType_name[ins.IGetType()] + " " + ins.IGetValue() + "\n\n\nTritium Stack\n=========\n\n" } // errString = errString + ctx.FileAndLine(ins) + "\n" if len(thisFile) > 0 && thisFile != "__rewriter__" { switch ins.IGetType() { case constants.Instruction_IMPORT: errString = errString + fmt.Sprintf("%s:%d", thisFile, ins.IGetLineNumber()) errString = errString + fmt.Sprintf(":\t@import %s\n", ctx.Transform.IGetNthObject(int(ins.IGetObjectId())).IGetName()) case constants.Instruction_FUNCTION_CALL: errString = errString + fmt.Sprintf("%s:%d", thisFile, ins.IGetLineNumber()) if callee := ins.IGetValue(); len(callee) > 0 { errString = errString + fmt.Sprintf(":\t%s\n", callee) } default: // do nothing } } } panic(errString) } }() ctx.Whale.Debugger.TrapInstruction(ctx.MessagePath, ctx.Filename, ctx.Env, ins, scope.Value, scope.Index, ctx.CurrentDoc) if time.Now().After(ctx.Deadline) && !ctx.InDebug { panic(TimeoutError) } indent := "" for i := 0; i < len(ctx.Yields); i++ { indent += "\t" } returnValue = "" switch ins.IGetType() { case constants.Instruction_BLOCK: for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) returnValue = ctx.RunInstruction(scope, child) } case constants.Instruction_TEXT: returnValue = ins.IGetValue() case constants.Instruction_LOCAL_VAR: name := ins.IGetValue() vars := ctx.Vars() if ins.INumArgs() > 0 { vars[name] = ctx.RunInstruction(scope, ins.IGetNthArgument(0)) } if ins.INumChildren() > 0 { ts := &Scope{Value: ctx.Vars()[name]} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } vars[name] = ts.Value } returnValue = vars[name] case constants.Instruction_IMPORT: obj := ctx.IGetNthObject(int(ins.IGetObjectId())) curFile := ctx.Filename ctx.Filename = obj.IGetName() ctx.Whale.Debugger.LogImport(ctx.MessagePath, ctx.Filename, curFile, int(ins.IGetLineNumber())) root := obj.IGetRoot() // TODO(layer-track): Currently we keep track of which layer we're in, but we // don't expose that to the user. If performance becomes an issue, we might // want to consider removing this bit. It would encompass removing the layer // meta-data from the protobuf object as well. The feature was introduce // for the case where maybe we'd want styles/assets to be included at a per // layer basis, but it was then decided that we don't want to do that anymore, // so this feature is left usecase-less. pushedLayer := false if layer := obj.IGetLayer(); layer != "" && layer != ctx.CurrentLayer() { ctx.PushLayer(layer) pushedLayer = true } for i := 0; i < root.INumChildren(); i++ { child := root.IGetNthChild(i) ctx.RunInstruction(scope, child) } //TODO(layer-track) -- see above if pushedLayer { ctx.PopLayer() } ctx.Whale.Debugger.LogImportDone(ctx.MessagePath, ctx.Filename, curFile, int(ins.IGetLineNumber())) ctx.Filename = curFile case constants.Instruction_FUNCTION_CALL: fun := ctx.Functions[int(ins.IGetFunctionId())] args := make([]interface{}, ins.INumArgs()) for i := 0; i < len(args); i++ { argIns := ins.IGetNthArgument(i) args[i] = ctx.RunInstruction(scope, argIns) } if fun.IGetBuiltIn() { curFile := ctx.Filename if f := builtInFunctions[fun.Name]; f != nil { returnValue = f(ctx, scope, ins, args) if returnValue == nil { returnValue = "" } } else { panic("missing function: " + fun.IGetName()) } ctx.Filename = curFile } else { // We are using a user-defined function //println("Resetting localvar") // Setup the new local var vars := make(map[string]interface{}, len(args)) for i := 0; i < fun.INumArgs(); i++ { arg := fun.IGetNthArg(i) vars[arg.IGetName()] = args[i] } yieldBlock := &YieldBlock{ Ins: ins, Vars: vars, Filename: ctx.Filename, } // PUSH! ctx.PushYieldBlock(yieldBlock) curFile := ctx.Filename ctx.Filename = fun.IGetFilename() // if it's a user-called function, save the curfile:linenumber // Are we going to need a stack here? --A.L. if !ctx.Debugger.IsProd() && ins.IGetIsUserCalled() == true { ctx.Env[isUserCalledEnvKey] = fmt.Sprintf("%s:%d", curFile, ins.IGetLineNumber()) } for i := 0; i < fun.IGetInstruction().INumChildren(); i++ { child := fun.IGetInstruction().IGetNthChild(i) returnValue = ctx.RunInstruction(scope, child) } if ins.IGetIsUserCalled() == true { delete(ctx.Env, isUserCalledEnvKey) } ctx.Filename = curFile // POP! ctx.PopYieldBlock() } } return }