internal object apply(bool callMe, JSObjectBase f, object t, JSObjectBase args, ExecutionContext x) { // Curse ECMA again! if (t == JSUndefined.Undefined || t == null) { t = x; } if (!(t is JSObjectBase)) { t = JSObject.ToObject(GLOBAL, t); } if (args == JSUndefined.Undefined || args == null) { args = new JSObject(); args.SetItem(GLOBAL, "length", new JSSimpleProperty("length", 0.0, false, false, true)); } else if (args is JSArray) { var vv = new JSObject(); int ii, jj; for (ii = 0, jj = (int)JSObject.ToNumber(GLOBAL, args.GetItem(GLOBAL, "length").GetValue(GLOBAL)); ii < jj; ii++) { vv.SetItem(GLOBAL, ii.ToString(), new JSSimpleProperty(ii.ToString(), args.GetItem(GLOBAL, ii.ToString()).GetValue(GLOBAL), false, false, true)); } vv.SetItem(GLOBAL, "length", new JSSimpleProperty("length", (double)ii, false, false, true)); args = vv; } if (callMe) { return(f.Call(GLOBAL, t, args, x)); } else { return(f.Construct(GLOBAL, args, x)); } }
internal object execute(Node n, ExecutionContext x) { int i, j; List <Node> aNode; ExecutionContext sEcon; JSObjectBase jaa; JSObjectBase f, tVal; object r, s, u = null, v = null; Node tNode = null, rNode = null, uNode = null; bool matchDefault = false; bool switchLoop; if (TraceExec) { UnityEngine.Debug.Log("Execute[" + n.ToString() + " => "); } try { switch (n.type) { case TokenType.Function: if (n.functionForm != StatementForm.DECLARED_FORM) { if (n.name == null || n.name == "" || n.functionForm == StatementForm.STATEMENT_FORM) { v = new FunctionObject(GLOBAL, n, x.scope); if (n.functionForm == StatementForm.STATEMENT_FORM) { x.scope.jobject.SetItem(GLOBAL, n.name, new JSSimpleProperty(n.name, v)); } } else { tVal = new JSObject(); ExecutionContext tmp = x.scope; x.scope = new ExecutionContext(); x.scope.jobject = tVal; x.scope.parent = tmp; try { v = new FunctionObject(GLOBAL, n, x.scope); tVal.SetItem(GLOBAL, n.name, new JSSimpleProperty(n.name, v, true, true)); } finally { x.scope = x.scope.parent; } } } break; case TokenType.SCRIPT: tVal = x.scope.jobject; aNode = n.funDecls; for (i = 0, j = aNode.Count; i < j; i++) { s = aNode[i].name; f = new FunctionObject(GLOBAL, aNode[i], x.scope); tVal.SetItem(GLOBAL, s.ToString(), new JSSimpleProperty(s.ToString(), f, x.type != CodeType.EVAL_CODE)); } aNode = n.varDecls; for (i = 0, j = aNode.Count; i < j; i++) { uNode = aNode[i]; s = uNode.name; if (uNode.readOnly && tVal.HasOwnProperty(s.ToString())) { throw new TypeError ("Redeclaration of const " + s, uNode.filename, uNode.lineno); } if (uNode.readOnly || !tVal.HasOwnProperty(s.ToString())) { tVal.SetItem(GLOBAL, s.ToString(), new JSSimpleProperty(s.ToString(), JSUndefined.Undefined, x.type != CodeType.EVAL_CODE, uNode.readOnly)); } } // FALL THROUGH for (i = 0, j = n.Count; i < j; i++) { execute(n[i], x); } break; case TokenType.BLOCK: for (i = 0, j = n.Count; i < j; i++) { execute(n[i], x); } break; case TokenType.If: if (JSObject.ToBool(GLOBAL, execute(n.condition, x))) { execute(n.thenPart, x); } else if (n.elsePart != null) { execute(n.elsePart, x); } break; case TokenType.Switch: s = Reference.GetValue(GLOBAL, execute(n.discriminant, x)); aNode = n.cases; Node tt; switchLoop = false; for (i = 0, j = aNode.Count; ; i++) { if (i == j) { if (n.defaultIndex >= 0) { i = n.defaultIndex - 1; // no case matched, do default matchDefault = true; continue; } break; // no default, exit switch_loop } tt = aNode[i]; // next case (might be default!) if (tt.type == TokenType.Case) { u = Reference.GetValue(GLOBAL, execute(tt.caseLabel, x)); } else { if (!matchDefault) // not defaulting, skip for now { continue; } u = s; // force match to do default } if (object.Equals(u, s)) { for (; ;) { // this loop exits switch_loop if (tt.statements != null) { try { execute(tt.statements, x); } catch (BreakException) { if (x.target == n) { switchLoop = true; break; } else { throw; } } } if (++i == j) { switchLoop = true; break; } tNode = aNode[i]; } // NOT REACHED } if (switchLoop) { break; } } break; case TokenType.For: if (n.setup != null) { Reference.GetValue(GLOBAL, execute(n.setup, x)); } // FALL THROUGH while (n.condition == null || JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute(n.condition, x)))) { try { execute(n.body, x); } catch (BreakException) { if (x.target == n) { break; } throw; } catch (ContinueException) { if (x.target == n) { if (n.update != null) { Reference.GetValue(GLOBAL, execute(n.update, x)); } if (n.condition != null && !JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute(n.condition, x)))) { break; } else { continue; } } throw; } if (n.update != null) { Reference.GetValue(GLOBAL, execute(n.update, x)); } } break; case TokenType.While: while (n.condition == null || JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute(n.condition, x)))) { try { execute(n.body, x); } catch (BreakException) { if (x.target == n) { break; } throw; } catch (ContinueException) { if (x.target == n) { continue; } throw; } if (n.update != null) { Reference.GetValue(GLOBAL, execute(n.update, x)); } } break; case TokenType.FOR_IN: uNode = n.varDecl; if (uNode != null) { execute(uNode, x); } rNode = n.iterator; v = Reference.GetValue(GLOBAL, execute(n.jobject, x)); // ECMA deviation to track extant browser JS implementation behavior. tVal = JSObject.ToObject(GLOBAL, v); i = 0; jaa = new JSArray(GLOBAL); foreach (string ii in tVal.Properties) { string istr = (i++).ToString(); jaa.SetItem(GLOBAL, istr, new JSSimpleProperty(istr, ii)); } for (j = i, i = 0; i < j; i++) { Reference.PutValue(GLOBAL, execute(rNode, x), jaa.GetItem(GLOBAL, i.ToString()).GetValue(GLOBAL)); try { execute(n.body, x); } catch (BreakException) { if (x.target == n) { break; } throw; } catch (ContinueException) { if (x.target == n) { continue; } throw; } } break; case TokenType.Do: do { try { execute(n.body, x); } catch (BreakException) { if (x.target != n) { break; } throw; } catch (ContinueException) { if (x.target != n) { continue; } throw; } } while (JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute(n.condition, x)))); break; case TokenType.Break: x.target = n.target; throw new BreakException(); case TokenType.Continue: x.target = ((Node)n).target; throw new ContinueException(); case TokenType.Try: try { execute(n.tryBlock, x); } catch (Exception exn) { ThrownException ex = exn as ThrownException; object e; if (ex != null) { e = ex.Value; } else { e = new JSInstanceWrapper(GLOBAL, exn); } j = n.catchClauses.Count; x.result = JSUndefined.Undefined; for (i = 0; ; i++) { if (i == j) { throw; } tNode = n.catchClauses[i]; ExecutionContext xscope = new ExecutionContext(); xscope.jobject = new JSObject(); xscope.jobject.SetItem(GLOBAL, tNode.varName, new JSSimpleProperty(tNode.varName, e, false, false, true)); xscope.parent = x.scope; x.scope = xscope; try { if (tNode.guard != null && !JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute(tNode.guard, x)))) { continue; } execute(tNode.block, x); break; } finally { x.scope = x.scope.parent; } } } finally { if (n.finallyBlock != null) { execute(n.finallyBlock, x); } } break; case TokenType.Throw: x.result = Reference.GetValue(GLOBAL, execute(n.exception, x)); throw new ThrownException((double)(int)TokenType.Throw); case TokenType.Return: x.result = n.valueNode == null ? JSUndefined.Undefined : Reference.GetValue(GLOBAL, execute(n.valueNode, x)); throw new ReturnException(); case TokenType.With: { r = execute(n.jobject, x); tVal = JSObject.ToObject(GLOBAL, Reference.GetValue(GLOBAL, r)); ExecutionContext tmp = x.scope; x.scope = new ExecutionContext(); x.scope.jobject = tVal; x.scope.parent = tmp; try { execute(n.body, x); } finally { x.scope = x.scope.parent; } } break; case TokenType.Var: case TokenType.Const: for (i = 0, j = n.Count; i < j; i++) { uNode = n[i].initializer; if (uNode == null) { continue; } string identName = n[i].name; for (sEcon = x.scope; sEcon != null; sEcon = sEcon.parent) { if (sEcon.jobject.HasOwnProperty(identName)) { break; } } u = Reference.GetValue(GLOBAL, execute(uNode, x)); if (n.type == TokenType.Const) { sEcon.jobject.SetItem(GLOBAL, identName, new JSSimpleProperty(identName, u, true, true, false)); } else { sEcon.jobject.SetItem(GLOBAL, identName, new JSSimpleProperty(identName, u)); } } break; case TokenType.Debugger: throw new Exception("NYI: debugger"); case TokenType.SEMICOLON: if (n.expression != null) { x.result = Reference.GetValue(GLOBAL, execute(n.expression, x)); } break; case TokenType.LABEL: try { execute(n.statement, x); } catch (BreakException) { if (x.target != n) { throw; } } break; case TokenType.COMMA: for (i = 0, j = n.Count; i < j; i++) { v = Reference.GetValue(GLOBAL, execute(n[i], x)); } break; case TokenType.ASSIGN: r = execute(n[0], x); TokenType tok = jsdefs.tokenWords[n.value.ToString()]; v = Reference.GetValue(GLOBAL, execute(n[1], x)); if (tok != TokenType.ASSIGN) { u = Reference.GetValue(GLOBAL, r); if (tok != TokenType.PLUS) { double ld = JSObject.ToNumber(GLOBAL, u), bd = JSObject.ToNumber(GLOBAL, v); long laa = (long)ld, bb = (long)bd; int bint = (int)bb; switch (tok) { case TokenType.BITWISE_OR: ld = laa | bb; break; case TokenType.BITWISE_XOR: ld = laa ^ bb; break; case TokenType.BITWISE_AND: ld = laa & bb; break; case TokenType.LSH: ld = laa << bint; break; case TokenType.RSH: ld = laa >> bint; break; case TokenType.URSH: ld = ((uint)laa) >> bint; break; case TokenType.MINUS: ld = ld - bd; break; case TokenType.MUL: ld = ld * bd; break; case TokenType.DIV: ld = ld / bd; break; case TokenType.MOD: ld = laa % bb; break; } v = ld; } else { v = Add(u, v); } } Reference.PutValue(GLOBAL, r, v); break; case TokenType.HOOK: { object res1 = Reference.GetValue(GLOBAL, execute(n[0], x)); v = JSObject.ToBool(GLOBAL, res1) ? Reference.GetValue(GLOBAL, execute(n[1], x)) : Reference.GetValue(GLOBAL, execute(n[2], x)); } break; case TokenType.OR: { object res1 = Reference.GetValue(GLOBAL, execute(n[0], x)); v = JSObject.ToBool(GLOBAL, res1) ? res1 : Reference.GetValue(GLOBAL, execute(n[1], x)); } break; case TokenType.AND: { object res1 = Reference.GetValue(GLOBAL, execute(n[0], x)); v = JSObject.ToBool(GLOBAL, res1) ? Reference.GetValue(GLOBAL, execute(n[1], x)) : res1; } break; case TokenType.BITWISE_OR: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) | (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.BITWISE_XOR: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) ^ (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.BITWISE_AND: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) & (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.EQ: v = Reference.GetValue(GLOBAL, execute(n[0], x)); u = Reference.GetValue(GLOBAL, execute(n[1], x)); if ((v == null || v == JSUndefined.Undefined) && (u == null || u == JSUndefined.Undefined)) { v = true; } else { v = JSObject.CompareTo(GLOBAL, v, u) == 0; } break; case TokenType.NE: if (((u == null || u is JSUndefined) && (v != null && !(v is JSUndefined))) || ((v == null || v is JSUndefined) && (u != null && !(u is JSUndefined)))) { return(true); } v = JSObject.CompareTo(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))) != 0; break; case TokenType.STRICT_EQ: v = Object.Equals(Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.STRICT_NE: v = !Object.Equals(Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.LT: v = JSObject.CompareTo(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))) == -1; break; case TokenType.LE: v = JSObject.CompareTo(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))) != 1; break; case TokenType.GE: v = JSObject.CompareTo(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))) != -1; break; case TokenType.GT: v = JSObject.CompareTo(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))) == 1; break; case TokenType.In: v = execute(n[1], x); tVal = Reference.GetValue(GLOBAL, v) as JSObjectBase; if (tVal == null) { throw new TypeError("invalid 'in' operand " + v.ToString()); } v = tVal.HasProperty(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x)).ToString()); break; case TokenType.Instanceof: u = Reference.GetValue(GLOBAL, execute(n[0], x)); tVal = Reference.GetValue(GLOBAL, execute(n[1], x)) as JSObjectBase; if (tVal == null) { throw new TypeError("Not an object in instanceof"); } v = tVal.HasInstance(GLOBAL, u); break; case TokenType.LSH: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) << (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.RSH: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) >> (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.URSH: v = (double)((int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) >> (int)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x)))); break; case TokenType.PLUS: v = Add(Reference.GetValue(GLOBAL, execute(n[0], x)), Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.MINUS: v = JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) - JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.MUL: v = JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) * JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.DIV: v = JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) / JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.MOD: v = JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[0], x))) % (long)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute(n[1], x))); break; case TokenType.Delete: { u = execute((Node)n[0], x); Reference refer = u as Reference; if (refer != null) { v = refer.GetBase().Delete(refer.GetPropertyName()); } } break; case TokenType.Void: Reference.GetValue(GLOBAL, execute((Node)n[0], x)); v = JSUndefined.Undefined; break; case TokenType.Typeof: u = execute((Node)n[0], x); if (u is Reference) { Reference refer = (Reference)u; if (refer.GetBase() == null) { v = JSUndefined.Undefined; break; } u = Reference.GetValue(GLOBAL, refer); } v = JSObject.Typeof(u).ToLower(); break; case TokenType.NOT: v = !JSObject.ToBool(GLOBAL, Reference.GetValue(GLOBAL, execute((Node)n[0], x))); break; case TokenType.BITWISE_NOT: v = (double)~(long)JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute((Node)n[0], x))); break; case TokenType.UNARY_PLUS: v = JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute((Node)n[0], x))); break; case TokenType.UNARY_MINUS: v = -JSObject.ToNumber(GLOBAL, Reference.GetValue(GLOBAL, execute((Node)n[0], x))); break; case TokenType.INCREMENT: case TokenType.DECREMENT: { object t = execute(n[0], x); u = Reference.GetValue(GLOBAL, t); if (n.postfix) { v = u; } Reference.PutValue(GLOBAL, t, n.type == TokenType.INCREMENT ? JSObject.ToNumber(GLOBAL, u) + 1 : JSObject.ToNumber(GLOBAL, u) - 1); if (!n.postfix) { v = u; } } break; case TokenType.DOT: { r = execute((Node)n[0], x); object t = Reference.GetValue(GLOBAL, r); u = n[1].value; v = new Reference(JSObject.ToObject(GLOBAL, t), u.ToString()); } break; case TokenType.INDEX: { r = execute(n[0], x); object t = Reference.GetValue(GLOBAL, r); u = Reference.GetValue(GLOBAL, execute(n[1], x)); v = new Reference(JSObject.ToObject(GLOBAL, t), u.ToString()); } break; case TokenType.LIST: // Curse ECMA for specifying that arguments is not an Array object! tVal = new JSObject(); int k = 0; CollectArguments(tVal, n, ref k, x); tVal.SetItem(GLOBAL, "length", new JSSimpleProperty("length", (double)k)); v = tVal; break; case TokenType.CALL: { r = execute(n[0], x); JSObjectBase args = (JSObjectBase)execute(n[1], x); object o = Reference.GetValue(GLOBAL, r); if (Object.ReferenceEquals(o, JSUndefined.Undefined)) { throw new TypeError("Can't call undefined " + n[0].ToString() + " " + r); } JSObjectBase fun = (JSObjectBase)o; tVal = (r is Reference) ? ((Reference)r).GetBase() : null; if (tVal is Activation) { tVal = null; } try { v = apply(true, fun, tVal, args, x); } catch (TypeError te) { if (r is Reference) { throw new TypeError(((Reference)r).GetPropertyName() + ": " + te.Message); } else { throw; } } } break; case TokenType.New: case TokenType.NEW_WITH_ARGS: { r = execute(n[0], x); JSObjectBase fun = (JSObjectBase)Reference.GetValue(GLOBAL, r); if (n.type == TokenType.New) { jaa = new JSArray(GLOBAL); } else { jaa = (JSObjectBase)execute(n[1], x); } v = fun.Construct(GLOBAL, jaa, x); } break; case TokenType.ARRAY_INIT: JSArray jaa1 = new JSArray(GLOBAL); for (i = 0, j = n.Count; i < j; i++) { if (n[i] != null) { jaa1[i] = new JSSimpleProperty(i.ToString(), Reference.GetValue(GLOBAL, execute(n[i], x))); } } jaa1.length = j; v = jaa1; break; case TokenType.OBJECT_INIT: tVal = new JSObject(); for (i = 0, j = n.Count; i < j; i++) { Node tx = n[i]; if (tx.type == TokenType.PROPERTY_INIT) { tVal.SetItem(GLOBAL, tx[0].value.ToString(), new JSSimpleProperty (tx[0].value.ToString(), Reference.GetValue(GLOBAL, execute(tx[1], x)))); } else { f = new FunctionObject(GLOBAL, tx, x.scope); string tname = tx.name; JSSimpleProperty prop; if (!tx.fields.TryGetValue(tname, out prop)) { tx.fields[tname] = new JSSimpleProperty(tname, v); } if (tx.type == TokenType.GETTER) { prop.DefineGetter(new Thunk(tVal, f, x)); } else { prop.DefineSetter(new Thunk(tVal, f, x)); } tVal.SetItem(GLOBAL, tname, prop); } } v = tVal; break; case TokenType.Null: v = null; break; case TokenType.This: v = x.thisOb; break; case TokenType.True: v = true; break; case TokenType.False: v = false; break; case TokenType.IDENTIFIER: { for (sEcon = x.scope; sEcon != null; sEcon = sEcon.parent) { if (sEcon.jobject != null && sEcon.jobject.HasProperty(GLOBAL, n.value.ToString())) { break; } } v = new Reference(sEcon != null ? sEcon.jobject : GLOBAL.jobject, n.value.ToString()); } break; case TokenType.NUMBER: case TokenType.STRING: case TokenType.REGEXP: v = n.value; break; case TokenType.GROUP: v = execute(n[0], x); break; default: throw new TypeError("PANIC: unknown operation " + n.type.ToString() + ": " + n.ToString()); } } catch (FalseReturn) { v = false; } catch (ContinueException) { throw; } catch (BreakException) { throw; } catch (ThrownException) { throw; } catch (ReturnException) { throw; } catch (Exception e) { throw new ScriptException(e.Message, n, e); } if (TraceExec) { UnityEngine.Debug.Log(v == null ? "(null)" : v.ToString() + " ]"); } return(v); }