/* ***** BEGIN LICENSE BLOCK ***** * Version: GPL 3 * * This program is Copyright (C) 2007-2008 Aptana, Inc. All Rights Reserved * This program is licensed under the GNU General Public license, version 3 (GPL). * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by the GPL, * is prohibited. * * You can redistribute and/or modify this program under the terms of the GPL, * as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * You may view the GPL, and Aptana's exception and additional terms in the file * titled license-jaxer.html in the main distribution folder of this program. * * Any modifications to this file must keep this entire header intact. * * ***** END LICENSE BLOCK ***** */ package com.aptana.ide.editor.scriptdoc.runtime; import java.util.ArrayList; import java.util.List; import java.util.Stack; import com.aptana.ide.editor.js.runtime.Environment; import com.aptana.ide.editor.js.runtime.IFunction; import com.aptana.ide.editor.js.runtime.IObject; import com.aptana.ide.lexer.IRange; import com.aptana.ide.lexer.Range; /** * @author Kevin Lindsey */ public class ScriptDocVM { private List<Opcode> _opcodes; private Stack<Object> _stack; /** * ScriptDocVM * * @param environment */ public ScriptDocVM() { this._opcodes = new ArrayList<Opcode>(); this._stack = new Stack<Object>(); } /** * addDuplicate */ public void addDuplicate() { this._opcodes.add(this.createDuplicate()); } /** * addGet * * @param fileIndex * @param fileOffset */ public void addGet(int fileIndex, int fileOffset) { this._opcodes.add(this.createGet(fileIndex, fileOffset)); } /** * addGetGlobal */ public void addGetGlobal() { this._opcodes.add(this.createGetGlobal()); } /** * addInstantiate * * @param fileIndex * @param range */ public void addInstantiate(int fileIndex, Range range) { this._opcodes.add(this.createInstantiate(fileIndex, range)); } /** * addInvoke * * @param fileIndex * @param range */ public void addInvoke(int fileIndex, Range range) { this._opcodes.add(this.createInvoke(fileIndex, range)); } /** * addNop */ public void addNoOperation() { this._opcodes.add(this.createNoOperation()); } /** * addOpcode * * @param opcode */ public void addOpcode(Opcode opcode) { this._opcodes.add(opcode); } /** * addOpcodes * * @param opcodes */ public void addOpcodes(List<Opcode> opcodes) { this._opcodes.addAll(opcodes); } /** * addPop */ public void addPop() { this._opcodes.add(this.createPop()); } /** * addPush * * @param value */ public void addPush(Object value) { this._opcodes.add(this.createPush(value)); } /** * addPushArray */ public void addPushArray(int index, IRange range) { this._opcodes.add(this.createPushArray(index, range)); } /** * addPushBoolean */ public void addPushBoolean(int index, IRange range) { this._opcodes.add(this.createPushBoolean(index, range)); } /** * addPushNull */ public void addPushNull(int index, IRange range) { this._opcodes.add(this.createPushNull(index, range)); } /** * addPushNumber */ public void addPushNumber(int index, IRange range) { this._opcodes.add(this.createPushNumber(index, range)); } /** * addPushObject */ public void addPushObject(int index, IRange range) { this._opcodes.add(this.createPushObject(index, range)); } /** * addPushRegExp */ public void addPushRegExp(int index, IRange range) { this._opcodes.add(this.createPushRegExp(index, range)); } /** * addPushString */ public void addPushString(int index, IRange range) { this._opcodes.add(this.createPushString(index, range)); } /** * addPut * * @param fileIndex */ public void addPut(int fileIndex) { this._opcodes.add(this.createPut(fileIndex)); } /** * addSwap */ public void addSwap() { this._opcodes.add(this.createSwap()); } /** * clearOpcodes */ public void clearOpcodes() { this._opcodes.clear(); } /** * clearStack */ public void clearStack() { this._stack.clear(); } /** * createDuplicate * * @return */ public Opcode createDuplicate() { return new Opcode(OpcodeType.DUPLICATE); } /** * createGet * * @param fileIndex * @param fileOffset * @return */ public Opcode createGet(int fileIndex, int fileOffset) { return new Opcode(OpcodeType.GET, fileIndex, fileOffset); } /** * createGetGlobal * * @return */ public Opcode createGetGlobal() { return new Opcode(OpcodeType.GET_GLOBAL); } /** * createInstantiate * * @param fileIndex * @param range * @return */ public Opcode createInstantiate(int fileIndex, Range range) { return new Opcode(OpcodeType.INSTANTIATE, fileIndex, range); } /** * createInvoke * * @param fileIndex * @param range * @return */ public Opcode createInvoke(int fileIndex, Range range) { return new Opcode(OpcodeType.INVOKE, fileIndex, range); } /** * createNoOperation * * @return */ public Opcode createNoOperation() { return new Opcode(OpcodeType.NO_OPERATION); } /** * createPop * * @return */ public Opcode createPop() { return new Opcode(OpcodeType.POP); } /** * createPush * * @param value * @return */ public Opcode createPush(Object value) { return new Opcode(OpcodeType.PUSH, value); } /** * createPushArray * * @return */ public Opcode createPushArray(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_ARRAY, fileIndex, range); } /** * createPushBoolean * * @return */ public Opcode createPushBoolean(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_BOOLEAN, fileIndex, range); } /** * createPushNull * * @return */ public Opcode createPushNull(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_NULL, fileIndex, range); } /** * createPushNumber * * @return */ public Opcode createPushNumber(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_NUMBER, fileIndex, range); } /** * createPushObject * * @return */ public Opcode createPushObject(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_OBJECT, fileIndex, range); } /** * createPushRegExp * * @return */ public Opcode createPushRegExp(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_REGEXP, fileIndex, range); } /** * createPushString * * @return */ public Opcode createPushString(int fileIndex, IRange range) { return new Opcode(OpcodeType.PUSH_STRING, fileIndex, range); } /** * createPut * * @param fileIndex * @return */ public Opcode createPut(int fileIndex) { return new Opcode(OpcodeType.PUT, fileIndex); } /** * createSwap * * @return */ public Opcode createSwap() { return new Opcode(OpcodeType.SWAP); } /** * execute * * @param environment */ public void execute(Environment environment) { Stack<Object> stack = this._stack; // locals used during execution IObject[] args = new IObject[0]; IObject object; IFunction function; String propertyName; for (Opcode opcode : this._opcodes) { switch (opcode.type) { case DUPLICATE: stack.push(stack.peek()); break; case GET: propertyName = (String) stack.pop(); object = (IObject) stack.pop(); stack.push(object.getPropertyValue(propertyName, opcode.fileIndex, opcode.fileOffset)); break; case GET_GLOBAL: stack.push(environment.getGlobal()); break; case INSTANTIATE: function = (IFunction) stack.pop(); stack.push(function.construct(environment, args, opcode.fileIndex, opcode.range)); break; case INVOKE: function = (IFunction) stack.pop(); stack.push(function.invoke(environment, args, opcode.fileIndex, opcode.range)); break; case NO_OPERATION: // do nothing break; case POP: stack.pop(); break; case PUT: IObject value = (IObject) stack.pop(); propertyName = (String) stack.pop(); object = (IObject) stack.pop(); object.putPropertyValue(propertyName, value, opcode.fileIndex); break; case PUSH: stack.push(opcode.value); break; case PUSH_ARRAY: stack.push(environment.createArray(opcode.fileIndex, opcode.range)); break; case PUSH_BOOLEAN: stack.push(environment.createBoolean(opcode.fileIndex, opcode.range)); break; case PUSH_NULL: // NOTE: seems like we should use fileIndex/IRange for null constants stack.push(environment.createNull()); break; case PUSH_NUMBER: stack.push(environment.createNumber(opcode.fileIndex, opcode.range)); break; case PUSH_OBJECT: stack.push(environment.createObject(opcode.fileIndex, opcode.range)); break; case PUSH_REGEXP: stack.push(environment.createRegExp(opcode.fileIndex, opcode.range)); break; case PUSH_STRING: stack.push(environment.createString(opcode.fileIndex, opcode.range)); break; case SWAP: int lastIndex = stack.size() - 1; stack.set(lastIndex, stack.set(lastIndex - 1, stack.peek())); break; default: break; } } } /** * getOpcodes * * @return */ public Opcode[] getOpcodes() { return this._opcodes.toArray(new Opcode[this._opcodes.size()]); } /** * getStackValues * * @return */ public Object[] getStackValues() { return this._stack.toArray(new Object[this._stack.size()]); } }