package org.dynjs.codegen; import me.qmx.jitescript.CodeBlock; import me.qmx.jitescript.internal.org.objectweb.asm.tree.LabelNode; import org.dynjs.compiler.bytecode.Chunker; import org.dynjs.exception.ThrowException; import org.dynjs.parser.Statement; import org.dynjs.parser.ast.*; import org.dynjs.runtime.*; import org.dynjs.runtime.builtins.types.BuiltinArray; import org.dynjs.runtime.builtins.types.BuiltinNumber; import org.dynjs.runtime.builtins.types.BuiltinObject; import org.dynjs.runtime.builtins.types.BuiltinRegExp; import org.dynjs.runtime.builtins.types.regexp.DynRegExp; import org.dynjs.runtime.interp.InterpretingVisitorFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; import static me.qmx.jitescript.util.CodegenUtils.*; public class BasicBytecodeGeneratingVisitor extends CodeGeneratingVisitor { private static final String[] EMPTY_STRING_ARRAY = {}; public BasicBytecodeGeneratingVisitor(InterpretingVisitorFactory interpFactory, BlockManager blockManager) { super(interpFactory, blockManager); } @Override public CodeBlock jsGetValue(final Class<?> throwIfNot) { CodeBlock codeBlock = new CodeBlock() // IN: reference .aload(Arities.EXECUTION_CONTEXT) // reference context .swap() // context reference .invokestatic(p(Types.class), "getValue", sig(Object.class, ExecutionContext.class, Object.class)); // value if (throwIfNot != null) { LabelNode end = new LabelNode(); codeBlock.dup() // value value .instance_of(p(throwIfNot)) // value bool .iftrue(end) // value .pop() .append(jsThrowTypeError("expected " + throwIfNot.getName())) .label(end) .nop(); } return codeBlock; } public Object visitPlus(ExecutionContext context, AdditiveExpression expr, boolean strict) { LabelNode doubleNums = new LabelNode(); LabelNode stringConcatByLeft = new LabelNode(); LabelNode stringConcat = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // ref(lhs) append(jsGetValue()); // val(lhs) aconst_null(); // val(lhs) null append(jsToPrimitive()); // val(lhs) dup(); // val(lhs) val(lhs) instance_of(p(String.class)); // val(lhs) bool expr.getRhs().accept(context, this, strict); // val(lhs) bool ref(rhs) append(jsGetValue()); // val(lhs) bool val(rhs) swap(); // val(lhs) val(rhs) bool iftrue(stringConcatByLeft); aconst_null(); // val(lhs) val(rhs) null append(jsToPrimitive()); // val(lhs) val(rhs) dup(); // val(lhs) val(rhs) val(rhs) instance_of(p(String.class)); // val(lhs) val(rhs) bool iftrue(stringConcat); // ---------------------------------------- // Numbers // val(lhs) val(rhs) append(jsToNumber()); swap(); append(jsToNumber()); swap(); // num(lhs) num(rhs) // Number(lhs) Number(rhs) append(ifEitherIsDouble(doubleNums)); // ---------------------------------------------- // Integral append(convertTopTwoToPrimitiveLongs()); // long(lhs) long(rhs) ladd(); // num(total) append(convertTopToLong()); // Long(total) go_to(end); // ---------------------------------------------- // Double label(doubleNums); // (doubles) Number(lhs) Number(rhs) append(convertTopTwoToPrimitiveDoubles()); // double(lhs) double(rhs) dadd(); // num(total) append(convertTopToDouble()); // Double(total) go_to(end); // ---------------------------------------- // Strings forced by LHS label(stringConcatByLeft); // val(lhs) val(rhs) aconst_null(); // val(lhs) val(rhs) null append(jsToPrimitive()); // val(lhs) val(rhs); // ---------------------------------------- // Strings label(stringConcat); // val(lhs) val(rhs) append(jsToString()); // val(lhs) str(rhs) swap(); // str(rhs) val(lhs) append(jsToString()); // str(lhs) str(lhs) swap(); // str(lhs) str(rhs) invokevirtual(p(String.class), "concat", sig(String.class, String.class)); // obj(concat) // ---------------------------------------- // TODO: Arrays // ---------------------------------------- label(end); nop(); return null; } public Object visitMinus(ExecutionContext context, AdditiveExpression expr, boolean strict) { LabelNode doubleNums = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // obj(lhs) append(jsGetValue()); // val(lhs) append(jsToNumber()); expr.getRhs().accept(context, this, strict); // val(lhs) obj(rhs) append(jsGetValue()); // val(lhs) val(rhs) append(jsToNumber()); append(ifEitherIsDouble(doubleNums)); // ------------------------------------- // Integral append(convertTopTwoToPrimitiveLongs()); lsub(); append(convertTopToLong()); go_to(end); // ------------------------------------- // Double label(doubleNums); append(convertTopTwoToPrimitiveDoubles()); dsub(); append(convertTopToDouble()); label(end); nop(); return null; } @Override public Object visit(Object context, BitwiseExpression expr, boolean strict) { expr.getLhs().accept(context, this, strict); append(jsGetValue()); // value if (expr.getOp().equals(">>>")) { append(jsToUint32()); } else { append(jsToInt32()); } // Number invokevirtual(p(Number.class), "longValue", sig(long.class)); // long expr.getRhs().accept(context, this, strict); append(jsGetValue()); // long value switch (expr.getOp()) { case "<<": case ">>": case ">>>": // 11.7.1 - 11.7.3 append(jsToUint32()); // long Number invokevirtual(p(Number.class), "longValue", sig(long.class)); // long long l2i(); // long int ldc(0x1F); // long int int iand(); // long int break; case "&": case "|": case "^": append(jsToInt32()); // long Number invokevirtual(p(Number.class), "longValue", sig(long.class)); // long long break; } if (expr.getOp().equals("<<")) { lshl(); // long l2i(); // int append(convertTopToInteger()); // Integer } else if (expr.getOp().equals(">>")) { lshr(); // long l2i(); // int append(convertTopToInteger()); // Integer } else if (expr.getOp().equals(">>>")) { lushr(); // long append(convertTopToLong()); // Long } else if (expr.getOp().equals("&")) { land(); // long append(convertTopToLong()); // Long } else if (expr.getOp().equals("|")) { lor(); // long append(convertTopToLong()); // Long } else if (expr.getOp().equals("^")) { lxor(); // long append(convertTopToLong()); // Long } return null; } @Override public Object visit(Object context, ArrayLiteralExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context invokestatic(p(BuiltinArray.class), "newArray", sig(DynArray.class, ExecutionContext.class)); // array int index = 0; for (Expression each : expr.getExprs()) { if (each != null) { dup(); // array array aload(Arities.EXECUTION_CONTEXT); // array array context ldc(index + ""); // array array context name each.accept(context, this, strict); // array array context name val append(jsGetValue()); // array array context name val invokestatic(p(PropertyDescriptor.class), "newPropertyDescriptorForObjectInitializer", sig(PropertyDescriptor.class, Object.class)); // array array context name desc iconst_0(); i2b(); // array array context name desc bool invokevirtual(p(DynArray.class), "defineOwnProperty", sig(boolean.class, ExecutionContext.class, String.class, PropertyDescriptor.class, boolean.class)); // array bool pop(); // array } ++index; } // array dup(); // array array aload(Arities.EXECUTION_CONTEXT); // array array context ldc("length"); // array array context name ldc((long) expr.getExprs().size()); // array array context name size invokestatic(p(Long.class), "valueOf", sig(Long.class, long.class)); // array array context name size iconst_0(); i2b(); // array array context name size bool invokeinterface(p(JSObject.class), "put", sig(void.class, ExecutionContext.class, String.class, Object.class, boolean.class)); // array return null; } @Override public Object visit(Object context, AssignmentExpression expr, boolean strict) { LabelNode throwRefError = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // reference dup(); // reference reference instance_of(p(Reference.class)); // reference bool iffalse(throwRefError); // reference checkcast(p(Reference.class)); expr.getRhs().accept(context, this, strict); // reference expr append(jsGetValue()); // reference value dup_x1(); // value reference value aload(Arities.EXECUTION_CONTEXT); // value reference value context swap(); // value reference context value invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // value go_to(end); label(throwRefError); // reference pop(); newobj(p(ThrowException.class)); // ex dup(); // ex ex aload(Arities.EXECUTION_CONTEXT); // ex ex context ldc(expr.getLhs().toString() + " is not a reference"); // ex ex context str invokevirtual(p(ExecutionContext.class), "createReferenceError", sig(JSObject.class, String.class)); // ex ex error aload(Arities.EXECUTION_CONTEXT); // ex ex error context swap(); // ex ex context error invokespecial(p(ThrowException.class), "<init>", sig(void.class, ExecutionContext.class, Object.class)); // ex ex athrow(); label(end); nop(); return null; } @Override public Object visit(Object context, BitwiseInversionOperatorExpression expr, boolean strict) { expr.getExpr().accept(context, this, strict); // obj append(jsGetValue()); // val append(jsToInt32()); // Long invokevirtual(p(Long.class), "longValue", sig(long.class)); // long ldc(-1L); // long -1(long) lxor(); // long invokestatic(p(Long.class), "valueOf", sig(Long.class, long.class)); // Long return null; } @Override public Object visit(Object context, BlockStatement statement, boolean strict) { // 12.1 LabelNode abrupt = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion astore(Arities.COMPLETION); // <empty> for (Statement each : statement.getBlockContent()) { if (each == null) { continue; } LabelNode nonAbrupt = new LabelNode(); LabelNode bringForwardValue = new LabelNode(); LabelNode nextStatement = new LabelNode(); if (each.getPosition() != null) { line(each.getPosition().getLine()); aload(Arities.EXECUTION_CONTEXT); // context ldc(each.getPosition().getLine()); // context line invokevirtual(p(ExecutionContext.class), "setLineNumber", sig(void.class, int.class)); // <empty> } if (each.getSizeMetric() > Chunker.STATEMENT_THRESHOLD) { interpretedStatement(each, strict); } else { each.accept(context, this, strict); } // completion(cur) dup(); // completion(cur) completion(cur) append(handleCompletion(nonAbrupt, abrupt, abrupt, abrupt)); // ---------------------------------------- // Non-abrupt label(nonAbrupt); // completion(cur); dup(); // completion(cur) completion(cur) append(jsCompletionValue()); // completion(cur) value ifnull(bringForwardValue); // completion(cur) astore(Arities.COMPLETION); // <empty> go_to(nextStatement); // ---------------------------------------- label(bringForwardValue); // completion(cur) dup(); // completion(cur) completion(cur) aload(Arities.COMPLETION); // completion(cur) completion(cur) completion(prev) append(jsCompletionValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) astore(Arities.COMPLETION); // <empty> label(nextStatement); } go_to(end); // ---------------------------------------- // ABRUPT label(abrupt); // completion(cur) astore(Arities.COMPLETION); // <empty> // ---------------------------------------- // END label(end); // <empty> aload(Arities.COMPLETION); return null; } @Override public Object visit(Object context, BooleanLiteralExpression expr, boolean strict) { if (expr.getValue()) { getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); } else { getstatic(p(Boolean.class), "FALSE", ci(Boolean.class)); } return null; } @Override public Object visit(Object context, BreakStatement statement, boolean strict) { breakCompletion(statement.getTarget()); return null; } @Override public Object visit(Object context, CaseClause clause, boolean strict) { clause.getBlock().accept(context, this, strict); return null; } @Override public Object visit(Object context, DefaultCaseClause clause, boolean strict) { clause.getBlock().accept(context, this, strict); return null; } @Override public Object visit(Object context, CatchClause clause, boolean strict) { clause.getBlock().accept(context, this, strict); return null; } @Override public Object visit(Object context, CompoundAssignmentExpression expr, boolean strict) { expr.getRootExpr().accept(context, this, strict); // value dup(); // value value expr.getRootExpr().getLhs().accept(context, this, strict); // value value reference swap(); // value reference value aload(Arities.EXECUTION_CONTEXT); // value reference value context swap(); // value reference context value invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // value return null; } @Override public Object visit(Object context, ContinueStatement statement, boolean strict) { continueCompletion(statement.getTarget()); return null; } @Override public Object visit(Object context, DeleteOpExpression expr, boolean strict) { LabelNode checkAsProperty = new LabelNode(); LabelNode handleEnvRec = new LabelNode(); LabelNode notMap = new LabelNode(); LabelNode returnTrue = new LabelNode(); LabelNode end = new LabelNode(); // ---------------------------------------- expr.getExpr().accept(context, this, strict); // ref dup(); // ref ref instance_of(p(Reference.class)); // ref bool iffalse(returnTrue); // ref checkcast(p(Reference.class)); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isUnresolvableReference", sig(boolean.class)); // ref bool iffalse(checkAsProperty); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isStrictReference", sig(boolean.class)); // ref bool iffalse(returnTrue); // ref append(jsThrowSyntaxError("unable to delete " + expr.getExpr())); // ref + throw go_to(returnTrue); // ---------------------------------------- // Check as property label(checkAsProperty); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isPropertyReference", sig(boolean.class)); // ref bool iffalse(handleEnvRec); // ref dup(); // ref ref append(jsGetBase()); // ref base dup(); // ref base base instance_of( p(JSObject.class) ); // ref base is-JSObject? iftrue( notMap ); // ref base dup(); // ref base base instance_of(p(Map.class)); // ref base is-Map iffalse( notMap ); // ref base checkcast(p(Map.class)); // ref map swap(); // map ref invokevirtual(p(Reference.class), "getReferencedName", sig(String.class)); // map name invokeinterface(p(Map.class), "remove", sig(Object.class, Object.class)); // prev-value go_to(returnTrue); // ---------------------------------------- label(notMap); // ref base append(jsToObject()); // ref obj swap(); // obj ref aload(Arities.EXECUTION_CONTEXT); // obj ref context swap(); // obj context ref dup(); // obj context ref ref invokevirtual(p(Reference.class), "getReferencedName", sig(String.class)); // obj context ref name swap(); // obj context name ref invokevirtual(p(Reference.class), "isStrictReference", sig(boolean.class)); // obj context name bool invokeinterface(p(JSObject.class), "delete", sig(boolean.class, ExecutionContext.class, String.class, boolean.class)); // bool invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); // Boolean go_to(end); // ---------------------------------------- // Environment record LabelNode throwSyntax = new LabelNode(); label(handleEnvRec); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isStrictReference", sig(boolean.class)); // ref bool iftrue(throwSyntax); // ref dup(); // ref ref append(jsGetBase()); // ref base checkcast(p(EnvironmentRecord.class)); // ref env-rec swap(); // env-rec ref invokevirtual(p(Reference.class), "getReferencedName", sig(String.class)); // env-rec name aload(Arities.EXECUTION_CONTEXT); // env-rec name context swap(); // env-rec context name invokeinterface(p(EnvironmentRecord.class), "deleteBinding", sig(boolean.class, ExecutionContext.class, String.class)); // bool invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); // Boolean go_to(end); label(throwSyntax); // ref append(jsThrowSyntaxError("unable to delete")); // ref go_to(end); // ---------------------------------------- // Simple true (with pop) label(returnTrue); // ref pop(); // <EMPTY> getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); // Boolean // ---------------------------------------- label(end); nop(); return null; } @Override public Object visit(Object context, DoWhileStatement statement, boolean strict) { LabelNode begin = new LabelNode(); LabelNode normalTarget = new LabelNode(); LabelNode breakTarget = new LabelNode(); LabelNode continueTarget = new LabelNode(); LabelNode end = new LabelNode(); label(begin); invokeCompiledStatementBlock("Do", statement.getBlock(), strict); // completion(block) dup(); // completion(block) completion(block) append(handleCompletion(normalTarget, breakTarget, continueTarget, end)); // ---------------------------------------- // NORMAL label(normalTarget); // completion(block) statement.getTest().accept(context, this, strict); // completion(block) result append(jsGetValue()); // completion(block) result append(jsToBoolean()); // completion(block) Boolean invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // completion(block) bool iffalse(end); pop(); // <EMPTY> go_to(begin); // ---------------------------------------- // BREAK label(breakTarget); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(continueTarget); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(normalTarget); // ---------------------------------------- label(end); // completion(block) nop(); // completion(block) return null; } @Override public Object visit(Object context, EmptyStatement statement, boolean strict) { normalCompletion(); return null; } @Override public Object visit(Object context, EqualityOperatorExpression expr, boolean strict) { LabelNode returnTrue = new LabelNode(); LabelNode returnFalse = new LabelNode(); LabelNode end = new LabelNode(); aload(Arities.EXECUTION_CONTEXT); // context expr.getLhs().accept(context, this, strict); // context obj(lhs) append(jsGetValue()); // context val(lhs) expr.getRhs().accept(context, this, strict); // context val(lhs) obj(rhs) append(jsGetValue()); // context val(lhs) val(rhs) invokestatic(p(Types.class), "compareEquality", sig(boolean.class, ExecutionContext.class, Object.class, Object.class)); // bool if (expr.getOp().equals("==")) { iftrue(returnTrue); go_to(returnFalse); } else { iffalse(returnTrue); go_to(returnFalse); } label(returnTrue); getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); go_to(end); label(returnFalse); getstatic(p(Boolean.class), "FALSE", ci(Boolean.class)); label(end); nop(); return null; } @Override public Object visit(Object context, CommaOperator expr, boolean strict) { expr.getLhs().accept(context, this, strict); jsGetValue(); pop(); expr.getRhs().accept(context, this, strict); jsGetValue(); return null; } @Override public Object visit(Object context, ExpressionStatement statement, boolean strict) { Expression expr = statement.getExpr(); if (expr instanceof FunctionDeclaration) { normalCompletion(); } else { expr.accept(context, this, strict); // value append(jsGetValue()); // value normalCompletionWithValue(); // Completion } return null; } @Override public Object visit(Object context, FloatingNumberExpression expr, boolean strict) { visit(context, (NumberLiteralExpression) expr, strict); return null; } @Override public Object visit(Object context, ForExprInStatement statement, boolean strict) { LabelNode nextName = new LabelNode(); LabelNode checkCompletion = new LabelNode(); LabelNode bringForward = new LabelNode(); LabelNode doBreak = new LabelNode(); LabelNode doContinue = new LabelNode(); LabelNode undefEnd = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion statement.getRhs().accept(context, this, strict); // completion val append(jsGetValue()); // completion val dup(); // completion val val append(jsPushUndefined()); // completion val val UNDEF if_acmpeq(undefEnd); // completion val dup(); // completion val val append(jsPushNull()); // completion val val NULL if_acmpeq(undefEnd); // completion val append(jsToObject()); // completion jsObj // ----------------------------------------------- // completion jsObj invokeinterface(p(JSObject.class), "getAllEnumerablePropertyNames", sig(NameEnumerator.class)); // completion name-enum astore(4); // completion label(nextName); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "hasNext", sig(boolean.class)); // completion bool iffalse(end); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "next", sig(String.class)); // completion str statement.getExpr().accept(context, this, strict); // completion str ref swap(); // completion ref str aload(Arities.EXECUTION_CONTEXT); // completion ref str context swap(); // completion ref context str invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // completion invokeCompiledStatementBlock("For", statement.getBlock(), strict); // completion(prev) completion(cur) dup(); // completion(prev) completion(cur) completion(cur) append(jsCompletionValue()); // completion(prev) completion(cur) val(cur) ifnull(bringForward); // completion(prev) completion(cur) // ---------------------------------- // has value swap(); // completion(cur) completion(prev) pop(); // completion(cur) go_to(checkCompletion); // ---------------------------------------- // bring previous value forward label(bringForward); // completion(prev) completion(cur) dup_x1(); // completion(cur) completion(prev) completion(cur) swap(); // completion(cur) completion(cur) completion(prev) append(jsGetValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) // ----------------------------------------------- label(checkCompletion); // completion(cur) dup(); // completion(cur) completion(cur) append(handleCompletion(nextName, doBreak, doContinue, end)); // completion // ---------------------------------------- // BREAK label(doBreak); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(doContinue); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(nextName); // ----------------------------------------------- // RHS is undefined // completion undef label(undefEnd); // completion undef pop(); // completion // ----------------------------------------------- label(end); // completion nop(); return null; } @Override public Object visit(Object context, ForExprOfStatement statement, boolean strict) { LabelNode nextName = new LabelNode(); LabelNode checkCompletion = new LabelNode(); LabelNode bringForward = new LabelNode(); LabelNode doBreak = new LabelNode(); LabelNode doContinue = new LabelNode(); LabelNode undefEnd = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion statement.getRhs().accept(context, this, strict); // completion val append(jsGetValue()); // completion val dup(); // completion val val append(jsPushUndefined()); // completion val val UNDEF if_acmpeq(undefEnd); // completion val dup(); // completion val val append(jsPushNull()); // completion val val NULL if_acmpeq(undefEnd); // completion val append(jsToObject()); // completion jsObj // ----------------------------------------------- // completion jsObj invokeinterface(p(JSObject.class), "getAllEnumerablePropertyNames", sig(NameEnumerator.class)); // completion name-enum astore(4); // completion label(nextName); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "hasNext", sig(boolean.class)); // completion bool iffalse(end); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "next", sig(String.class)); // completion str append(jsToObject()); // completion str jsObj swap(); // completion jsObj str invokevirtual(p(ExecutionContext.class), "createPropertyReference", sig(Reference.class, JSObject.class, String.class)); // completion val statement.getExpr().accept(context, this, strict); // completion val ref swap(); // completion ref val aload(Arities.EXECUTION_CONTEXT); // completion ref val context swap(); // completion ref context val invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // completion invokeCompiledStatementBlock("For", statement.getBlock(), strict); // completion(prev) completion(cur) dup(); // completion(prev) completion(cur) completion(cur) append(jsCompletionValue()); // completion(prev) completion(cur) val(cur) ifnull(bringForward); // completion(prev) completion(cur) // ---------------------------------- // has value swap(); // completion(cur) completion(prev) pop(); // completion(cur) go_to(checkCompletion); // ---------------------------------------- // bring previous value forward label(bringForward); // completion(prev) completion(cur) dup_x1(); // completion(cur) completion(prev) completion(cur) swap(); // completion(cur) completion(cur) completion(prev) append(jsGetValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) // ----------------------------------------------- label(checkCompletion); // completion(cur) dup(); // completion(cur) completion(cur) append(handleCompletion(nextName, doBreak, doContinue, end)); // completion // ---------------------------------------- // BREAK label(doBreak); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(doContinue); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(nextName); // ----------------------------------------------- // RHS is undefined // completion undef label(undefEnd); // completion undef pop(); // completion // ----------------------------------------------- label(end); // completion nop(); return null; } @Override public Object visit(Object context, ForVarDeclInStatement statement, boolean strict) { LabelNode nextName = new LabelNode(); LabelNode checkCompletion = new LabelNode(); LabelNode bringForward = new LabelNode(); LabelNode doBreak = new LabelNode(); LabelNode doContinue = new LabelNode(); LabelNode undefEnd = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion statement.getRhs().accept(context, this, strict); // completion val append(jsGetValue()); // completion val dup(); // completion val val append(jsPushUndefined()); // completion val val UNDEF if_acmpeq(undefEnd); // completion val dup(); // completion val val append(jsPushNull()); // completion val val NULL if_acmpeq(undefEnd); // completion val append(jsToObject()); // completion jsObj // ----------------------------------------------- // completion jsObj invokeinterface(p(JSObject.class), "getAllEnumerablePropertyNames", sig(NameEnumerator.class)); // completion name-enum astore(4); // completion label(nextName); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "hasNext", sig(boolean.class)); // completion bool iffalse(end); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "next", sig(String.class)); // completion str statement.getDeclaration().accept(context, this, strict); // completion pop(); // <EMPTY> aload(Arities.EXECUTION_CONTEXT); // context ldc(statement.getDeclaration().getIdentifier()); // context identifier invokevirtual(p(ExecutionContext.class), "resolve", sig(Reference.class, String.class)); // reference // completion str ref swap(); // completion ref str aload(Arities.EXECUTION_CONTEXT); // completion ref str context swap(); // completion ref context str invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // completion invokeCompiledStatementBlock("For", statement.getBlock(), strict); // completion(prev) completion(cur) dup(); // completion(prev) completion(cur) completion(cur) append(jsCompletionValue()); // completion(prev) completion(cur) val(cur) ifnull(bringForward); // completion(prev) completion(cur) // ---------------------------------- // has value swap(); // completion(cur) completion(prev) pop(); // completion(cur) go_to(checkCompletion); // ---------------------------------------- // bring previous value forward label(bringForward); // completion(prev) completion(cur) dup_x1(); // completion(cur) completion(prev) completion(cur) swap(); // completion(cur) completion(cur) completion(prev) append(jsGetValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) // ----------------------------------------------- label(checkCompletion); // completion(cur) dup(); // completion(cur) completion(cur) append(handleCompletion(nextName, doBreak, doContinue, end)); // completion // ---------------------------------------- // BREAK label(doBreak); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(doContinue); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(nextName); // ----------------------------------------------- // RHS is undefined // completion undef label(undefEnd); // completion undef pop(); // completion // ----------------------------------------------- label(end); // completion nop(); return null; } @Override public Object visit(Object context, ForVarDeclOfStatement statement, boolean strict) { LabelNode nextName = new LabelNode(); LabelNode checkCompletion = new LabelNode(); LabelNode bringForward = new LabelNode(); LabelNode doBreak = new LabelNode(); LabelNode doContinue = new LabelNode(); LabelNode undefEnd = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion statement.getRhs().accept(context, this, strict); // completion val append(jsGetValue()); // completion val dup(); // completion val val append(jsPushUndefined()); // completion val val UNDEF if_acmpeq(undefEnd); // completion val dup(); // completion val val append(jsPushNull()); // completion val val NULL if_acmpeq(undefEnd); // completion val append(jsToObject()); // completion jsObj // ----------------------------------------------- // completion jsObj invokeinterface(p(JSObject.class), "getAllEnumerablePropertyNames", sig(NameEnumerator.class)); // completion name-enum astore(4); // completion label(nextName); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "hasNext", sig(boolean.class)); // completion bool iffalse(end); // completion aload(4); // completion name-enum invokevirtual(p(NameEnumerator.class), "next", sig(String.class)); // completion str append(jsToObject()); // completion str jsObj swap(); // completion jsObj str invokevirtual(p(ExecutionContext.class), "createPropertyReference", sig(Reference.class, JSObject.class, String.class)); // completion val statement.getDeclaration().accept(context, this, strict); // completion pop(); // <EMPTY> aload(Arities.EXECUTION_CONTEXT); // context ldc(statement.getDeclaration().getIdentifier()); // context identifier invokevirtual(p(ExecutionContext.class), "resolve", sig(Reference.class, String.class)); // reference // completion str ref swap(); // completion ref str aload(Arities.EXECUTION_CONTEXT); // completion ref str context swap(); // completion ref context str invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // completion invokeCompiledStatementBlock("For", statement.getBlock(), strict); // completion(prev) completion(cur) dup(); // completion(prev) completion(cur) completion(cur) append(jsCompletionValue()); // completion(prev) completion(cur) val(cur) ifnull(bringForward); // completion(prev) completion(cur) // ---------------------------------- // has value swap(); // completion(cur) completion(prev) pop(); // completion(cur) go_to(checkCompletion); // ---------------------------------------- // bring previous value forward label(bringForward); // completion(prev) completion(cur) dup_x1(); // completion(cur) completion(prev) completion(cur) swap(); // completion(cur) completion(cur) completion(prev) append(jsGetValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) // ----------------------------------------------- label(checkCompletion); // completion(cur) dup(); // completion(cur) completion(cur) append(handleCompletion(nextName, doBreak, doContinue, end)); // completion // ---------------------------------------- // BREAK label(doBreak); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(doContinue); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(nextName); // ----------------------------------------------- // RHS is undefined // completion undef label(undefEnd); // completion undef pop(); // completion // ----------------------------------------------- label(end); // completion nop(); return null; } @Override public Object visit(Object context, ForExprStatement statement, boolean strict) { if (statement.getExpr() != null) { statement.getExpr().accept(context, this, strict); pop(); } visitFor(context, statement, strict); return null; } @Override public Object visit(Object context, ForVarDeclStatement statement, boolean strict) { List<VariableDeclaration> decls = statement.getDeclarationList(); for (VariableDeclaration each : decls) { each.accept(context, this, strict); pop(); } visitFor(context, statement, strict); return null; } public Object visitFor(Object context, AbstractForStatement statement, boolean strict) { LabelNode begin = new LabelNode(); LabelNode bringForward = new LabelNode(); LabelNode hasValue = new LabelNode(); LabelNode checkCompletion = new LabelNode(); LabelNode doIncrement = new LabelNode(); LabelNode doBreak = new LabelNode(); LabelNode doContinue = new LabelNode(); LabelNode end = new LabelNode(); normalCompletion(); // completion label(begin); if (statement.getTest() != null) { statement.getTest().accept(context, this, strict); append(jsGetValue()); append(jsToBoolean()); invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // completion bool iffalse(end); // completion } // completion(prev) invokeCompiledStatementBlock("For", statement.getBlock(), strict); // completion(prev) completion(cur) dup(); // completion(prev) completion(cur) completion(cur) append(jsCompletionValue()); // completion(prev) completion(cur) val(cur) ifnull(bringForward); // completion(prev) completion(cur) go_to(hasValue); // ---------------------------------------- // bring previous forward label(bringForward); // completion(prev) completion(cur) dup_x1(); // completion(cur) completion(prev) completion(cur) swap(); // completion(cur) completion(cur) completion(prev) append(jsCompletionValue()); // completion(cur) completion(cur) val(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion(cur) go_to(checkCompletion); // ---------------------------------------- // has value label(hasValue); // completion(prev) completion(cur) swap(); // completion(cur) completion(prev) pop(); // completion(cur) // ---------------------------------------- // handle current completion label(checkCompletion); // completion dup(); // completion completion append(handleCompletion(doIncrement, /* break */doBreak, /* continue */doContinue, /* return */end)); // ---------------------------------------- // do increment label(doIncrement); // completion if (statement.getIncrement() != null) { statement.getIncrement().accept(context, this, strict); append(jsGetValue()); pop(); } // completion go_to(begin); // ---------------------------------------- // BREAK label(doBreak); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(doContinue); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(doIncrement); label(end); // completion return null; } @Override public Object visit(Object context, FunctionCallExpression expr, boolean strict) { LabelNode propertyRef = new LabelNode(); LabelNode noSelf = new LabelNode(); LabelNode doCall = new LabelNode(); LabelNode isCallable = new LabelNode(); // 11.2.3 aload(Arities.EXECUTION_CONTEXT); // context expr.getMemberExpression().accept(context, this, strict); // context ref dup(); // context ref ref append(jsGetValue()); // context ref function swap(); // context function ref dup(); // context function ref ref dup_x2(); // context ref function ref ref instance_of(p(Reference.class)); // context ref function ref isref? iffalse(noSelf); // ---------------------------------------- // Reference // context ref function ref checkcast(p(Reference.class)); dup(); // context ref function ref ref invokevirtual(p(Reference.class), "isPropertyReference", sig(boolean.class)); // context ref function ref bool(is-prop) iftrue(propertyRef); // ---------------------------------------- // Environment Record // context ref function ref append(jsGetBase()); // context ref function base checkcast(p(EnvironmentRecord.class)); // context ref function env-rec invokeinterface(p(EnvironmentRecord.class), "implicitThisValue", sig(Object.class)); // context ref function self go_to(doCall); // ---------------------------------------- // Property Reference label(propertyRef); // context ref function ref append(jsGetBase()); // context ref function self go_to(doCall); // ------------------------------------------ // No self label(noSelf); // context ref function ref pop(); // context ref function append(jsPushUndefined()); // context ref function UNDEFINED // ------------------------------------------ // call() label(doCall); // context ref function self swap(); // context ref self function List<Expression> argExprs = expr.getArgumentExpressions(); int numArgs = argExprs.size(); bipush(numArgs); anewarray(p(Object.class)); // context ref self function array for (int i = 0; i < numArgs; ++i) { dup(); bipush(i); argExprs.get(i).accept(context, this, strict); append(jsGetValue()); aastore(); } // context ref self function array swap(); // context ref self array function dup_x2(); // context ref function self array function invokestatic(p(Types.class), "isCallable", sig(boolean.class, Object.class)); // context ref function self array bool iftrue(isCallable); // context ref function self array append(jsThrowTypeError(expr.getMemberExpression() + " is not a function")); // THROWN! label(isCallable); // context ref function self array // call ExecutionContext#call(ref, fn, self, args) -> Object invokevirtual(p(ExecutionContext.class), "call", sig(Object.class, Object.class, JSFunction.class, Object.class, Object[].class)); // obj return null; } @Override public Object visit(Object context, FunctionDeclaration statement, boolean strict) { normalCompletion(); return null; } @Override public Object visit(Object context, FunctionExpression expr, boolean strict) { compiledFunction(expr.getDescriptor().getIdentifier(), expr.getDescriptor().getFormalParameterNames(), expr.getDescriptor().getBlock(), expr.getDescriptor() .isStrict()); return null; } @Override public Object visit(Object context, IdentifierReferenceExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context ldc(expr.getIdentifier()); // context identifier invokevirtual(p(ExecutionContext.class), "resolve", sig(Reference.class, String.class)); // reference return null; } @Override public Object visit(Object context, IfStatement statement, boolean strict) { LabelNode elseBranch = new LabelNode(); LabelNode noElseBranch = new LabelNode(); LabelNode end = new LabelNode(); statement.getTest().accept(context, this, strict); // value append(jsGetValue()); // value append(jsToBoolean()); // Boolean invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // bool if (statement.getElseBlock() == null) { // completion bool iffalse(noElseBranch); } else { iffalse(elseBranch); } // <empty> // ---------------------------------------- // THEN if (statement.getThenBlock() != null) { invokeCompiledStatementBlock("Then", statement.getThenBlock(), strict); } else { normalCompletion(); } // completion go_to(end); // ---------------------------------------- // ELSE if (statement.getElseBlock() == null) { label(noElseBranch); normalCompletion(); } else { label(elseBranch); // <empty> invokeCompiledStatementBlock("Else", statement.getElseBlock(), strict); // completion } label(end); // completion nop(); return null; } @Override public Object visit(Object context, InOperatorExpression expr, boolean strict) { LabelNode typeError = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // obj(lhs) append(jsGetValue()); // val(lhs) expr.getRhs().accept(context, this, strict); // val(lhs) obj(rhs) append(jsGetValue()); // val(lhs) val(rhs) dup(); // val(lhs) val(rhs) val(rhs) instance_of(p(JSObject.class)); // val(lhs) val(rhs) bool iffalse(typeError); // val(lhs) val(rhs) checkcast(p(JSObject.class)); // val(lhs) obj(rhs) swap(); // obj(rhs) val(lhs) append(jsToString()); // obj(rhs) str(lhs) aload(Arities.EXECUTION_CONTEXT); // obj(rhs) str(lhs) context swap(); // object(rhs) context str(lhs); invokeinterface(p(JSObject.class), "hasProperty", sig(boolean.class, ExecutionContext.class, String.class)); // bool go_to(end); label(typeError); // val(lhs) val(rhs) pop(); pop(); iconst_0(); i2b(); // bool append(jsThrowTypeError("not an object")); label(end); invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); // Boolean return null; } @Override public Object visit(Object context, OfOperatorExpression expr, boolean strict) { LabelNode typeError = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // obj(lhs) append(jsGetValue()); // val(lhs) expr.getRhs().accept(context, this, strict); // val(lhs) obj(rhs) append(jsGetValue()); // val(lhs) val(rhs) dup(); // val(lhs) val(rhs) val(rhs) instance_of(p(JSObject.class)); // val(lhs) val(rhs) bool iffalse(typeError); // val(lhs) val(rhs) checkcast(p(JSObject.class)); // val(lhs) obj(rhs) swap(); // obj(rhs) val(lhs) append(jsToString()); // obj(rhs) str(lhs) aload(Arities.EXECUTION_CONTEXT); // obj(rhs) str(lhs) context swap(); // object(rhs) context str(lhs); invokeinterface(p(JSObject.class), "hasProperty", sig(boolean.class, ExecutionContext.class, String.class)); // bool go_to(end); label(typeError); // val(lhs) val(rhs) pop(); pop(); iconst_0(); i2b(); // bool append(jsThrowTypeError("not an object")); label(end); invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); // Boolean return null; } @Override public Object visit(Object context, InstanceofExpression expr, boolean strict) { LabelNode typeError = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // obj(lhs) append(jsGetValue()); // val(lhs) expr.getRhs().accept(context, this, strict); // val(lhs) obj(rhs) append(jsGetValue()); // val(lhs) val(rhs) dup(); // val(lhs) val(rhs) val(rhs) instance_of(p(JSFunction.class)); // val(lhs) val(rhs) bool iffalse(typeError); // val(lhs) val(rhs) checkcast(p(JSFunction.class)); // val(lhs) fn(rhs) swap(); // fn(rhs) val(lhs) aload(Arities.EXECUTION_CONTEXT); // fn(rhs) val(lhs) context swap(); // fn(fhs) context val(lhs) invokeinterface(p(JSFunction.class), "hasInstance", sig(boolean.class, ExecutionContext.class, Object.class)); // bool go_to(end); label(typeError); // val(lhs) val(rhs) pop(); pop(); iconst_0(); i2b(); // bool append(jsThrowTypeError("not an object")); label(end); invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); // Boolean return null; } @Override public Object visit(Object context, IntegerNumberExpression expr, boolean strict) { visit(context, (NumberLiteralExpression) expr, strict); return null; } @Override public Object visit(Object context, LogicalExpression expr, boolean strict) { LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); append(jsGetValue()); dup(); // val(lhs) val(lhs) append(jsToBoolean()); // val(lhs) bool(lhs) invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // val(lhs) bool(lhs) if (expr.getOp().equals("&&")) { iffalse(end); } else if (expr.getOp().equals("||")) { iftrue(end); } pop(); // <empty> expr.getRhs().accept(context, this, strict); // val(rhs) append(jsGetValue()); // val(rhs) go_to(end); // ---------------------------------------- label(end); // val nop(); return null; } @Override public Object visit(Object context, LogicalNotOperatorExpression expr, boolean strict) { LabelNode returnFalse = new LabelNode(); LabelNode end = new LabelNode(); expr.getExpr().accept(context, this, strict); // obj append(jsGetValue()); // val append(jsToBoolean()); // Boolean invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // bool iftrue(returnFalse); iconst_1(); go_to(end); label(returnFalse); iconst_0(); label(end); invokestatic(p(Boolean.class), "valueOf", sig(Boolean.class, boolean.class)); nop(); return null; } @Override public Object visit(Object context, DotExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context expr.getLhs().accept(context, this, strict); // context reference append(jsGetValue()); // context object ldc(expr.getIdentifier()); // context object identifier swap(); // context identifier obj append(jsCheckObjectCoercible(null)); // context identifier obj swap(); // context object identifier append(jsCreatePropertyReference()); // reference return null; } @Override public Object visit(Object context, BracketExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context expr.getLhs().accept(context, this, strict); // context reference append(jsGetValue()); // context object expr.getRhs().accept(context, this, strict); // context object ident-expr swap(); // context ident-expr obj append(jsCheckObjectCoercible(null)); // context ident-expr obj swap(); // context object ident-expr append(jsGetValue()); // context object ident-obj append(jsToString()); // context object ident-str append(jsCreatePropertyReference()); // reference return null; } @Override public Object visit(Object context, MultiplicativeExpression expr, boolean strict) { LabelNode doubleNums = new LabelNode(); LabelNode returnNaN = new LabelNode(); LabelNode end = new LabelNode(); expr.getLhs().accept(context, this, strict); // val(lhs) append(jsGetValue()); append(jsToNumber()); expr.getRhs().accept(context, this, strict); // val(rhs) append(jsGetValue()); append(jsToNumber()); // val(lhs) val(rhs) append(ifEitherIsNaN(returnNaN)); if (expr.getOp().equals("%")) { append(ifTopIsZero(returnNaN)); } if (!expr.getOp().equals("/")) { append(ifEitherIsDouble(doubleNums)); if (expr.getOp().equals("*")) { append(convertTopTwoToPrimitiveLongs()); lmul(); append(convertTopToLong()); } else if (expr.getOp().equals("/")) { append(convertTopTwoToPrimitiveLongs()); ldiv(); append(convertTopToLong()); } else if (expr.getOp().equals("%")) { invokestatic(p(BuiltinNumber.class), "modulo", sig(Number.class, Number.class, Number.class)); } go_to(end); label(doubleNums); } append(convertTopTwoToPrimitiveDoubles()); if (expr.getOp().equals("*")) { dmul(); } else if (expr.getOp().equals("/")) { ddiv(); } else if (expr.getOp().equals("%")) { drem(); } append(convertTopToDouble()); go_to(end); label(returnNaN); pop(); pop(); getstatic(p(Double.class), "NaN", ci(double.class)); invokestatic(p(Double.class), "valueOf", sig(Double.class, double.class)); label(end); nop(); return null; } @Override public Object visit(Object context, NewOperatorExpression expr, boolean strict) { LabelNode end = new LabelNode(); // 11.2.2 expr.getExpr().accept(context, this, strict); // obj aload(Arities.EXECUTION_CONTEXT); // obj context swap(); // context obj append(jsGetValue(JSFunction.class)); // context ctor-fn bipush(0); anewarray(p(Object.class)); // context function array invokevirtual(p(ExecutionContext.class), "construct", sig(Object.class, JSFunction.class, Object[].class)); // obj label(end); nop(); return null; } @Override public Object visit(Object context, NullLiteralExpression expr, boolean strict) { getstatic(p(Types.class), "NULL", ci(Types.Null.class)); return null; } public Object visit(Object context, NumberLiteralExpression expr, boolean strict) { String text = expr.getText(); if (text.indexOf('.') == 0) { ldc("0" + text); invokestatic(p(Double.class), "valueOf", sig(Double.class, String.class)); } else if (text.indexOf(".") > 0) { ldc(text); ldc(10); invokestatic(p(Types.class), "parseLongOrDouble", sig(Number.class, String.class, int.class)); // Long or Double } else { if (text.startsWith("0x") || text.startsWith("0X")) { String realText = text.substring(2); ldc(realText); bipush(expr.getRadix()); invokestatic(p(Long.class), "valueOf", sig(Long.class, String.class, int.class)); // Long } else { final int index = text.toLowerCase().indexOf('e'); if (index > 0) { // scientific notation, but without the java-friendlyness. E.g. 1E21 instead of 1.0e21 String base = text.substring(0, index); String exponent = text.substring(index); String javafied = base + ".0" + exponent; ldc(javafied); invokestatic(p(Double.class), "valueOf", sig(Double.class, String.class)); } else { ldc(text); bipush(expr.getRadix()); invokestatic(p(Types.class), "parseLongOrDouble", sig(Number.class, String.class, int.class)); // Long or Double } } } return null; } @Override public Object visit(Object context, ObjectLiteralExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context invokestatic(p(BuiltinObject.class), "newObject", sig(DynObject.class, ExecutionContext.class)); // obj for (PropertyAssignment each : expr.getPropertyAssignments()) { dup(); // obj obj each.accept(context, this, strict); } // obj return null; } @Override public Object visit(Object context, PostOpExpression expr, boolean strict) { LabelNode doubleNum = new LabelNode(); LabelNode invalid = new LabelNode(); LabelNode end = new LabelNode(); expr.getExpr().accept(context, this, strict); // obj dup(); // obj obj instance_of(p(Reference.class)); // ref bool iffalse(invalid); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isValidForPrePostIncrementDecrement", sig(boolean.class)); // ref bool iffalse(invalid); // ref dup(); // ref ref append(jsGetValue()); // ref value append(jsToNumber()); // ref number dup(); // ref number number instance_of(p(Double.class)); // ref number bool iftrue(doubleNum); // ---------------------------------------- // Long // ref number dup2(); // ref Long ref Long invokevirtual(p(Number.class), "longValue", sig(long.class)); // ref Long ref long ldc(1L); // ref Long ref long 1 if (expr.getOp().equals("++")) { ladd(); } else { lsub(); } // ref Long(orig) ref long(new) invokestatic(p(Long.class), "valueOf", sig(Long.class, long.class)); // ref Long(orig) ref Long(new) aload(Arities.EXECUTION_CONTEXT); // ref Long(orig) ref Long(new) context swap(); // ref Long(orig) ref context Long(new) invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // ref Long(orig) swap(); // Long(orig) ref pop(); // Long(orig) go_to(end); // ---------------------------------------- // Double label(doubleNum); // ref number dup2(); // ref Double ref Double invokevirtual(p(Number.class), "doubleValue", sig(double.class)); // ref Double ref double iconst_1(); // ref Double ref double 1 i2d(); // ref Double ref double 1.0 if (expr.getOp().equals("++")) { dadd(); } else { dsub(); } // ref Double(orig) ref double(new) invokestatic(p(Double.class), "valueOf", sig(Double.class, double.class)); // ref Double(orig) ref Double(new) aload(Arities.EXECUTION_CONTEXT); // ref Double(orig) ref Double(new) context swap(); // ref Double(orig) ref context Double(new) invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // ref Double(orig) swap(); // Double(orig) ref pop(); // Double(orig) go_to(end); // ---------------------------------------- // Invalid label(invalid); // ref append(jsThrowSyntaxError("invalid operation")); label(end); nop(); return null; } @Override public Object visit(Object context, PreOpExpression expr, boolean strict) { LabelNode storeNewValue = new LabelNode(); LabelNode doubleNum = new LabelNode(); LabelNode invalid = new LabelNode(); LabelNode end = new LabelNode(); expr.getExpr().accept(context, this, strict); // obj dup(); // obj obj instance_of(p(Reference.class)); // ref bool iffalse(invalid); // ref dup(); // ref ref invokevirtual(p(Reference.class), "isValidForPrePostIncrementDecrement", sig(boolean.class)); // ref bool iffalse(invalid); // ref dup(); // ref ref dup(); // ref ref ref append(jsGetValue()); // ref ref value append(jsToNumber()); // ref ref number dup(); // ref ref number number instance_of(p(Double.class)); // ref ref number bool iftrue(doubleNum); // ref ref number // ---------------------------------------- // Integral // ref ref number invokevirtual(p(Number.class), "longValue", sig(long.class)); // ref ref long ldc(1L); // ref ref long 1L if (expr.getOp().equals("++")) { ladd(); } else { lsub(); } // ref ref long invokestatic(p(Long.class), "valueOf", sig(Long.class, long.class)); // ref ref Long go_to(storeNewValue); // ---------------------------------------- // Double label(doubleNum); // ref ref number invokevirtual(p(Number.class), "doubleValue", sig(double.class)); // ref ref double iconst_1(); // ref ref double 1 i2d(); // ref ref double 1.0 if (expr.getOp().equals("++")) { dadd(); } else { dsub(); } // ref ref double invokestatic(p(Double.class), "valueOf", sig(Double.class, double.class)); // ref ref Double label(storeNewValue); // ref ref newval aload(Arities.EXECUTION_CONTEXT); // ref ref newval context swap(); // ref ref context newval invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // ref append(jsGetValue()); // value go_to(end); label(invalid); // ref append(jsThrowSyntaxError("invalid operation")); label(end); nop(); return null; } @Override public Object visit(Object context, PropertyGet propertyGet, boolean strict) { // IN obj dup(); // obj obj aload(Arities.EXECUTION_CONTEXT); // obj obj context ldc(propertyGet.getName()); // obj obj context name invokeinterface(p(JSObject.class), "getOwnProperty", sig(Object.class, ExecutionContext.class, String.class)); // obj desc(orig) compiledFunction(null, EMPTY_STRING_ARRAY, propertyGet.getBlock(), false); // obj desc(orig) fn ldc(propertyGet.getName()); // obj desc(orig) fn name swap(); // obj desc(orig) name fn invokestatic(p(PropertyDescriptor.class), "newPropertyDescriptorForObjectInitializerGet", sig(PropertyDescriptor.class, Object.class, String.class, JSFunction.class)); // obj desc(new) aload(Arities.EXECUTION_CONTEXT); // obj desc(new) context swap(); // obj context desc(new) ldc(propertyGet.getName()); // obj context desc(new) name swap(); // obj context name desc(new) iconst_0(); // obj context name desc(new) 0 i2b(); // obj context name desc(new) false invokeinterface(p(JSObject.class), "defineOwnProperty", sig(boolean.class, ExecutionContext.class, String.class, PropertyDescriptor.class, boolean.class)); // bool pop(); // <EMPTY> return null; } @Override public Object visit(Object context, PropertySet propertySet, boolean strict) { // IN obj dup(); // obj obj aload(Arities.EXECUTION_CONTEXT); // obj obj context ldc(propertySet.getName()); // obj obj context name invokeinterface(p(JSObject.class), "getOwnProperty", sig(Object.class, ExecutionContext.class, String.class)); // obj desc(orig) compiledFunction(null, new String[] { propertySet.getIdentifier() }, propertySet.getBlock(), false); // obj desc(orig) fn ldc(propertySet.getName()); // obj desc(orig) fn name swap(); // obj desc(orig) name fn invokestatic(p(PropertyDescriptor.class), "newPropertyDescriptorForObjectInitializerSet", sig(PropertyDescriptor.class, Object.class, String.class, JSFunction.class)); // obj desc(new) aload(Arities.EXECUTION_CONTEXT); // obj desc(new) context swap(); // obj context desc(new) ldc(propertySet.getName()); // obj context desc(new) name swap(); // obj context name desc(new) iconst_0(); // obj context name desc(new) 0 i2b(); // obj context name desc(new) false invokeinterface(p(JSObject.class), "defineOwnProperty", sig(boolean.class, ExecutionContext.class, String.class, PropertyDescriptor.class, boolean.class)); // bool pop(); // <EMPTY> return null; } @Override public Object visit(Object context, NamedValue namedValue, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // obj obj context ldc(namedValue.getName()); // obj obj context name namedValue.getExpr().accept(context, this, strict); // obj obj context name val append(jsGetValue()); // obj obj context name val if (namedValue.getExpr() instanceof FunctionExpression) { ldc(namedValue.getName()); swap(); invokestatic(p(PropertyDescriptor.class), "newPropertyDescriptorForObjectInitializer", sig(PropertyDescriptor.class, String.class, Object.class)); } else { invokestatic(p(PropertyDescriptor.class), "newPropertyDescriptorForObjectInitializer", sig(PropertyDescriptor.class, Object.class)); } // obj obj context name desc iconst_0(); // obj obj context name desc 0 i2b(); // obj obj context name desc false invokeinterface(p(JSObject.class), "defineOwnProperty", sig(boolean.class, ExecutionContext.class, String.class, PropertyDescriptor.class, boolean.class)); // obj bool pop(); // obj return null; } @Override public Object visit(Object context, RegexpLiteralExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context ldc(expr.getPattern()); // context pattern ldc(expr.getFlags()); // context pattern flags invokestatic(p(BuiltinRegExp.class), "newRegExp", sig(DynRegExp.class, ExecutionContext.class, Object.class, String.class)); // regexp return null; } @Override public Object visit(Object context, RelationalExpression expr, boolean strict) { LabelNode returnFalse = new LabelNode(); LabelNode end = new LabelNode(); aload(Arities.EXECUTION_CONTEXT); expr.getLhs().accept(context, this, strict); append(jsGetValue()); expr.getRhs().accept(context, this, strict); append(jsGetValue()); // context lhs rhs if (expr.getOp().equals(">") || expr.getOp().equals("<=")) { swap(); iconst_0(); i2b(); // y x false } else { iconst_1(); i2b(); } invokestatic(p(Types.class), "compareRelational", sig(Object.class, ExecutionContext.class, Object.class, Object.class, boolean.class)); // result dup(); // result result if (expr.getOp().equals("<") || expr.getOp().equals(">")) { // result result append(jsPushUndefined()); // result result UNDEF if_acmpeq(returnFalse); // result go_to(end); } else if (expr.getOp().equals("<=") || expr.getOp().equals(">=")) { // result result append(jsPushUndefined()); // result result UNDEF if_acmpeq(returnFalse); // result dup(); // result result getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); // result result TRUE if_acmpeq(returnFalse); // result(FALSE) pop(); // <empty> getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); // TRUE go_to(end); } // ---------------------------------------- // FALSE label(returnFalse); // result pop(); getstatic(p(Boolean.class), "FALSE", ci(Boolean.class)); go_to(end); // ---------------------------------------- label(end); nop(); return null; } @Override public Object visit(Object context, ReturnStatement statement, boolean strict) { // 12.9 if (statement.getExpr() == null) { append(jsPushUndefined()); } else { statement.getExpr().accept(context, this, strict); append(jsGetValue()); } returnCompletion(); return null; } @Override public Object visit(Object context, StrictEqualityOperatorExpression expr, boolean strict) { LabelNode returnTrue = new LabelNode(); LabelNode returnFalse = new LabelNode(); LabelNode end = new LabelNode(); aload(Arities.EXECUTION_CONTEXT); // context expr.getLhs().accept(context, this, strict); // context obj(lhs) append(jsGetValue()); // context val(lhs) expr.getRhs().accept(context, this, strict); // context val(lhs) obj(rhs) append(jsGetValue()); // context val(lhs) val(rhs) invokestatic(p(Types.class), "compareStrictEquality", sig(boolean.class, ExecutionContext.class, Object.class, Object.class)); // bool if (expr.getOp().equals("===")) { iftrue(returnTrue); go_to(returnFalse); } else { iffalse(returnTrue); go_to(returnFalse); } label(returnTrue); getstatic(p(Boolean.class), "TRUE", ci(Boolean.class)); go_to(end); label(returnFalse); getstatic(p(Boolean.class), "FALSE", ci(Boolean.class)); label(end); nop(); return null; } @Override public Object visit(Object context, StringLiteralExpression expr, boolean strict) { ldc(expr.getLiteral()); return null; } @Override public Object visit(Object context, SwitchStatement statement, boolean strict) { LabelNode end = new LabelNode(); normalCompletion(); // completion astore(Arities.COMPLETION); // <empty> statement.getExpr().accept(context, this, strict); // switchref append(jsGetValue()); // switchval List<CaseClause> caseClauses = statement.getCaseClauses(); List<LabelNode> labels = new ArrayList<>(); int numClauses = caseClauses.size(); int defaultIndex = -1; // switchval for (int i = 0; i < numClauses; ++i) { CaseClause eachCase = caseClauses.get(i); LabelNode caseLabel = new LabelNode(); labels.add(caseLabel); if (eachCase instanceof DefaultCaseClause) { defaultIndex = i; continue; } LabelNode notMatched = new LabelNode(); dup(); // switchval switchval aload(Arities.EXECUTION_CONTEXT); // switchval switchval context swap(); // switchval context switchval eachCase.getExpression().accept(context, this, strict); // switchval context switchval caseref append(jsGetValue()); // switchval context switchval caseval invokestatic(p(Types.class), "compareStrictEquality", sig(boolean.class, ExecutionContext.class, Object.class, Object.class)); // switchval bool iffalse(notMatched); // switchval pop(); // <empty> go_to(caseLabel); label(notMatched); // switchval } // switchval pop(); // <empty> if (defaultIndex >= 0) { go_to(labels.get(defaultIndex)); } else { go_to(end); } for (int i = 0; i < numClauses; ++i) { LabelNode eachLabel = labels.get(i); label(eachLabel); CaseClause eachCase = caseClauses.get(i); invokeCompiledStatementBlock("Case", eachCase.getBlock(), strict); // <completion> LabelNode normal = new LabelNode(); LabelNode broke = new LabelNode(); LabelNode abrupt = new LabelNode(); LabelNode caseEnd = new LabelNode(); // completion dup(); // completion completion append(handleCompletion(normal, broke, abrupt, abrupt)); // ---------------------------------------- // NORMAL // ---------------------------------------- label(normal); // completion dup(); // completion completion append(jsCompletionValue()); // completion value ifnonnull(caseEnd); // completion dup(); // completion completion aload(Arities.COMPLETION); // completion completion completion(prev) append(jsCompletionValue()); // completion completion value(prev) putfield(p(Completion.class), "value", ci(Object.class)); // completion go_to( caseEnd ); // ---------------------------------------- // BREAK // ---------------------------------------- label(broke); // completion convertToNormalCompletion(); // completion // ---------------------------------------- // ABRUPT // ---------------------------------------- label(abrupt); // completion astore(Arities.COMPLETION); // <empty> go_to(end); // ---------------------------------------- // case-end // ---------------------------------------- label(caseEnd); // completion astore(Arities.COMPLETION); // <empty> } label(end); // <empty> aload(Arities.COMPLETION); // completion return null; } @Override public Object visit(Object context, TernaryExpression expr, boolean strict) { LabelNode elseBranch = new LabelNode(); LabelNode end = new LabelNode(); expr.getTest().accept(context, this, strict); // val append(jsGetValue()); // val append(jsToBoolean()); // Boolean invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // bool iffalse(elseBranch); // <empty> expr.getThenExpr().accept(context, this, strict); // thenval go_to(end); label(elseBranch); expr.getElseExpr().accept(context, this, strict); // elseval label(end); nop(); return null; } @Override public Object visit(Object context, ThisExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); invokevirtual(p(ExecutionContext.class), "getThisBinding", sig(Object.class)); return null; } @Override public Object visit(Object context, ThrowStatement statement, boolean strict) { statement.getExpr().accept(context, this, strict); append(jsGetValue()); // val newobj(p(ThrowException.class)); // val ex dup_x1(); // ex val ex swap(); // ex ex val aload(Arities.EXECUTION_CONTEXT); // ex ex val context swap(); // ex ex context val invokespecial(p(ThrowException.class), "<init>", sig(void.class, ExecutionContext.class, Object.class)); // ex athrow(); return null; } @Override public Object visit(Object context, TryStatement statement, boolean strict) { LabelNode end = new LabelNode(); LabelNode tryStart = new LabelNode(); LabelNode tryEnd = new LabelNode(); LabelNode outerCatchHandler = new LabelNode(); label(tryStart); invokeCompiledStatementBlock("Try", statement.getTryBlock(), strict); label(tryEnd); // completion(try) if (statement.getFinallyBlock() != null) { // completion(try) invokeCompiledStatementBlock("Finally", statement.getFinallyBlock(), strict); // completion(try) completion(finally) dup(); // completion(try) completion(finally) completion(finally) getfield(p(Completion.class), "type", ci(Completion.Type.class)); // completion(try) completion(finally) type(finally) getstatic(p(Completion.Type.class), "NORMAL", ci(Completion.Type.class)); // completion(try) completion(finally) type(finally) NORMAL LabelNode normalFinally = new LabelNode(); if_acmpeq(normalFinally); // ---------------------------------------- // Abnormal // completion(try) completion(finally) swap(); // completion(finally) completion(try) pop(); // completion(finally) go_to(end); // ---------------------------------------- // Normal label(normalFinally); // completion(try) completion(finally) pop(); // completion(try) } go_to(end); trycatch(tryStart, tryEnd, outerCatchHandler, p(ThrowException.class)); if (statement.getCatchClause() != null) { LabelNode catchCatchHandler = new LabelNode(); LabelNode catchStart = new LabelNode(); LabelNode catchEnd = new LabelNode(); label(outerCatchHandler); // ex invokevirtual(p(ThrowException.class), "getValue", sig(Object.class)); // thrown aload(Arities.EXECUTION_CONTEXT); // thrown context swap(); // context thrown compiledStatementBlock("Catch", statement.getCatchClause().getBlock(), strict); // context thrown catchblock swap(); // context catchblock thrown ldc(statement.getCatchClause().getIdentifier()); // context catchblock thrown ident swap(); // context catchblock ident thrown label(catchStart); invokevirtual(p(ExecutionContext.class), "executeCatch", sig(Completion.class, BasicBlock.class, String.class, Object.class)); label(catchEnd); // completion(catch) if (statement.getFinallyBlock() != null) { // completion(catch) invokeCompiledStatementBlock("Finally", statement.getFinallyBlock(), strict); // completion(catch) completion(finally) dup(); // completion(catch) completion(finally) completion(finally) getfield(p(Completion.class), "type", ci(Completion.Type.class)); // completion(catch) completion(finally) type(finally) getstatic(p(Completion.Type.class), "NORMAL", ci(Completion.Type.class)); // completion(catch) completion(finally) type(finally) NORMAL LabelNode normalFinally = new LabelNode(); if_acmpeq(normalFinally); // ---------------------------------------- // Abnormal // completion(catch) completion(finally) swap(); // completion(finally) completion(catch) pop(); // completion(finally) go_to(end); // ---------------------------------------- // Normal label(normalFinally); // completion(catch) completion(finally) pop(); // completion(catch) go_to(end); // ---------------------------------------- // IN CASE CATCH ITSELF THROWS // ---------------------------------------- LabelNode normalFinallyAfterThrow = new LabelNode(); trycatch(catchStart, catchEnd, catchCatchHandler, null); label(catchCatchHandler); // ex invokeCompiledStatementBlock("Finally", statement.getFinallyBlock(), strict); // ex completion(finally) dup(); // ex completion(finally) completion(finally) getfield(p(Completion.class), "type", ci(Completion.Type.class)); // ex completion(finally) type(finally) getstatic(p(Completion.Type.class), "NORMAL", ci(Completion.Type.class)); // ex completion(finally) type(finally) NORMAL if_acmpeq(normalFinallyAfterThrow); // ex completion(finally) swap(); // completion(finally) ex pop(); // completion(finally) go_to(end); label(normalFinallyAfterThrow); // ex completion(finally) pop(); // ex athrow(); } } else { // No catch, but thrown label(outerCatchHandler); // ex if (statement.getFinallyBlock() != null) { // ex invokeCompiledStatementBlock("Finally", statement.getFinallyBlock(), strict); // ex completion(finally) dup(); // ex completion(finally) completion(finally) getfield(p(Completion.class), "type", ci(Completion.Type.class)); // ex completion(finally) type(finally) getstatic(p(Completion.Type.class), "NORMAL", ci(Completion.Type.class)); // ex completion(finally) type(finally) NORMAL LabelNode normalFinally = new LabelNode(); if_acmpeq(normalFinally); // ---------------------------------------- // Abnormal // ex completion(finally) swap(); // completion(finally) ex pop(); // completion(finally) go_to(end); // ---------------------------------------- // Normal label(normalFinally); // ex completion(finally) pop(); // ex athrow(); // <THROWN> } } label(end); // completion nop(); return null; } @Override public Object visit(Object context, TypeOfOpExpression expr, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context expr.getExpr().accept(context, this, strict); // context obj invokestatic(p(Types.class), "typeof", sig(String.class, ExecutionContext.class, Object.class)); // string return null; } @Override public Object visit(Object context, UnaryMinusExpression expr, boolean strict) { LabelNode doubleNum = new LabelNode(); LabelNode zero = new LabelNode(); LabelNode end = new LabelNode(); expr.getExpr().accept(context, this, strict); // val append(jsGetValue()); // val append(jsToNumber()); // num dup(); // num num instance_of(p(Double.class)); // num bool iftrue(doubleNum); // num dup(); // num num invokevirtual(p(Number.class), "longValue", sig(long.class)); // num long ldc(0L); // num long 0L lcmp(); // num bool iffalse(zero); // ------------------------------------ // Integral // num(Long) invokevirtual(p(Number.class), "longValue", sig(long.class)); // num(long) ldc(-1L); // num -1L lmul(); // -num invokestatic(p(Long.class), "valueOf", sig(Long.class, long.class)); // -num(Long) go_to(end); // ------------------------------------ // Double label(doubleNum); // num(Doube) invokevirtual(p(Number.class), "doubleValue", sig(double.class)); // num(double) iconst_m1(); // num -1 i2d(); // num -1.0 dmul(); // -num invokestatic(p(Double.class), "valueOf", sig(Double.class, double.class)); // -num(Double) go_to(end); // ------------------------------------ label(zero); // num pop(); ldc(-0.0); invokestatic(p(Double.class), "valueOf", sig(Double.class, double.class)); label(end); nop(); return null; } @Override public Object visit(Object context, UnaryPlusExpression expr, boolean strict) { // 11.4.6 expr.getExpr().accept(context, this, strict); // val append(jsGetValue()); // val append(jsToNumber()); return null; } @Override public Object visit(Object context, VariableDeclaration expr, boolean strict) { if (expr.getExpr() == null) { ldc(expr.getIdentifier()); // str } else { append(jsResolve(expr.getIdentifier())); // reference aload(Arities.EXECUTION_CONTEXT); // reference context expr.getExpr().accept(context, this, strict); // reference context val append(jsGetValue()); // reference context val invokevirtual(p(Reference.class), "putValue", sig(void.class, ExecutionContext.class, Object.class)); // reference ldc(expr.getIdentifier()); // str } return null; } @Override public Object visit(Object context, VariableStatement statement, boolean strict) { for (VariableDeclaration each : statement.getVariableDeclarations()) { each.accept(context, this, strict); // identifier pop(); // <EMPTY> } normalCompletion(); return null; } @Override public Object visit(Object context, VoidOperatorExpression expr, boolean strict) { expr.getExpr().accept(context, this, strict); append(jsGetValue()); pop(); append(jsPushUndefined()); return null; } @Override public Object visit(Object context, WhileStatement statement, boolean strict) { LabelNode end = new LabelNode(); LabelNode breakTarget = new LabelNode(); LabelNode continueTarget = new LabelNode(); LabelNode begin = new LabelNode(); normalCompletion(); // completion(block) label(begin); statement.getTest().accept(context, this, strict); // completion(block) result append(jsGetValue()); // completion(block) result append(jsToBoolean()); // completion(block) Boolean invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); // completion(block) bool iffalse(end); // completion(block) invokeCompiledStatementBlock("Do", statement.getBlock(), strict); // completion(block,prev) completion(block,cur) swap(); // completion(block,cur) completion(block,prev) pop(); // completion(block,cur) dup(); // completion(block) completion(block) append(handleCompletion(begin, breakTarget, continueTarget, end)); // completion(block) // ---------------------------------------- // BREAK label(breakTarget); // completion(block,BREAK) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion convertToNormalCompletion(); // completion(block,NORMAL) go_to(end); // ---------------------------------------- // CONTINUE label(continueTarget); // completion(block,CONTINUE) dup(); // completion completion append(jsCompletionTarget()); // completion target append(statement.isInLabelSet()); // completion bool iffalse(end); // completion go_to(begin); // ---------------------------------------- label(end); // completion(block) nop(); return null; } @Override public Object visit(Object context, WithStatement statement, boolean strict) { aload(Arities.EXECUTION_CONTEXT); // context statement.getExpr().accept(context, this, strict); // context val append(jsGetValue()); // context val append(jsToObject()); // context obj compiledStatementBlock("With", statement.getBlock(), strict); // context obj block invokevirtual(p(ExecutionContext.class), "executeWith", sig(Completion.class, JSObject.class, BasicBlock.class)); // completion return null; } }