Esempio n. 1
0
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
}