Skip to content

Commit

Permalink
Support Computed Property Name
Browse files Browse the repository at this point in the history
* Invalid evaluating in order
* Not support destructing assignment
* When computed property set to `__proto__`, [[Prototype]] changed.
  • Loading branch information
uchida_t committed Sep 8, 2015
1 parent 836ce22 commit 0fef518
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 40 deletions.
24 changes: 23 additions & 1 deletion src/org/mozilla/javascript/CodeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1082,9 +1082,30 @@ private void visitLiteral(Node node, Node child)
} else {
throw badTree(node);
}
if (type == Token.OBJECTLIT) {
addIndexOp(Icode_LITERAL_KEY_NEW, count);
stackChange(1);
}
addIndexOp(Icode_LITERAL_NEW, count);
stackChange(2);
int i = 0;
while (child != null) {
if (type == Token.OBJECTLIT) {
Object id = propertyIds[i++];
if (id instanceof String) {
addStringOp(Token.STRING, (String)id);
stackChange(1);
} else if (id instanceof Number) {
int index = getDoubleIndex(((Number)id).doubleValue());
addIndexOp(Token.NUMBER, index);
stackChange(1);
} else {
visitExpression((Node)id, 0);
}
addIcode(Icode_LITERAL_KEY_SET);
stackChange(-1);
}

int childType = child.getType();
if (childType == Token.GET) {
visitExpression(child.getFirstChild(), 0);
Expand All @@ -1111,12 +1132,13 @@ private void visitLiteral(Node node, Node child)
literalIds.add(skipIndexes);
addIndexOp(Icode_SPARE_ARRAYLIT, index);
}
stackChange(-1);
} else {
int index = literalIds.size();
literalIds.add(propertyIds);
addIndexOp(Token.OBJECTLIT, index);
stackChange(-2);
}
stackChange(-1);
}

private void visitArrayComprehension(Node node, Node initStmt, Node expr)
Expand Down
13 changes: 12 additions & 1 deletion src/org/mozilla/javascript/IRFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,18 @@ private Node transformObjectLiteral(ObjectLiteral node) {
} else if (prop.isNormalMethod()) {
decompiler.addToken(Token.METHOD);
}
if (prop.isComputed()) {
decompiler.addToken(Token.LB);
}

properties[i++] = getPropKey(prop.getLeft());
if (prop.isComputed()) {
properties[i++] = transform(prop.getLeft());
} else {
properties[i++] = getPropKey(prop.getLeft());
}
if (prop.isComputed()) {
decompiler.addToken(Token.RB);
}

// OBJECTLIT is used as ':' in object literal for
// decompilation to solve spacing ambiguity.
Expand All @@ -900,6 +910,7 @@ private Node transformObjectLiteral(ObjectLiteral node) {
}
}
decompiler.addToken(Token.RC);
// TODO : add Node tree for computed property name.
object.putProp(Node.OBJECT_IDS_PROP, properties);
return object;
}
Expand Down
8 changes: 7 additions & 1 deletion src/org/mozilla/javascript/Icode.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ abstract class Icode {

Icode_DEBUGGER = -64,

// Object Literal Key (for Computed property name)
Icode_LITERAL_KEY_NEW = -65,
Icode_LITERAL_KEY_SET = -66,

// Last icode
MIN_ICODE = -64;
MIN_ICODE = -66;

static String bytecodeName(int bytecode)
{
Expand Down Expand Up @@ -217,6 +221,8 @@ static String bytecodeName(int bytecode)
case Icode_GENERATOR: return "GENERATOR";
case Icode_GENERATOR_END: return "GENERATOR_END";
case Icode_DEBUGGER: return "DEBUGGER";
case Icode_LITERAL_KEY_NEW: return "LITERAL_KEY_NEW";
case Icode_LITERAL_KEY_SET: return "LITERAL_KEY_SET";
}

// icode without name
Expand Down
20 changes: 19 additions & 1 deletion src/org/mozilla/javascript/Interpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1765,15 +1765,33 @@ private static Object interpretLoop(Context cx, CallFrame frame,
sDbl[stackTop] = i + 1;
continue Loop;
}
case Icode_LITERAL_KEY_NEW: {
++stackTop;
stack[stackTop] = new Object[indexReg];
sDbl[stackTop] = 0;
continue Loop;
}
case Icode_LITERAL_KEY_SET : {
Object value = stack[stackTop];
if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
--stackTop;
int i = (int)sDbl[stackTop];
((Object[])stack[stackTop - 2])[i] = ScriptRuntime.toPropertyKey(value, cx);
continue Loop;
}
case Token.ARRAYLIT :
case Icode_SPARE_ARRAYLIT :
case Token.OBJECTLIT : {
Object[] data = (Object[])stack[stackTop];
--stackTop;
int[] getterSetters = (int[])stack[stackTop];
Object[] ids = null;
if (op == Token.OBJECTLIT) {
--stackTop;
ids = (Object[])stack[stackTop];
}
Object val;
if (op == Token.OBJECTLIT) {
Object[] ids = (Object[])frame.idata.literalIds[indexReg];
val = ScriptRuntime.newObjectLiteral(ids, data, getterSetters, cx,
frame.scope);
} else {
Expand Down
12 changes: 12 additions & 0 deletions src/org/mozilla/javascript/NodeTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,18 @@ else if (last.getType() == Token.NAME &&
}
break;
}

case Token.OBJECTLIT: {
Object[] properties = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
for (Object prop : properties) {
if (prop instanceof Node) {
transformCompilationUnit_r(tree, (Node)prop, scope,
createScopeObjects,
inStrictMode);
}
}
break;
}
}

transformCompilationUnit_r(tree, node,
Expand Down
82 changes: 52 additions & 30 deletions src/org/mozilla/javascript/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3327,10 +3327,9 @@ private GeneratorExpressionLoop generatorExpressionLoop()
}
}

private static final int PROP_ENTRY = 1;
private static final int GET_ENTRY = 2;
private static final int SET_ENTRY = 4;
private static final int METHOD_ENTRY = 8;
private enum PropertyEntryKind {
LITERAL, GET, SET, METHOD
}

private ObjectLiteral objectLiteral()
throws IOException
Expand All @@ -3349,7 +3348,7 @@ private ObjectLiteral objectLiteral()
commaLoop:
for (;;) {
String propertyName = null;
int entryKind = PROP_ENTRY;
PropertyEntryKind entryKind = PropertyEntryKind.LITERAL;
int tt = peekToken();
Comment jsdocNode = getAndResetJsDoc();

Expand All @@ -3358,7 +3357,7 @@ private ObjectLiteral objectLiteral()
warnTrailingComma(pos, elems, afterComma);
break commaLoop;
} else {
AstNode pname = objliteralProperty();
ObjectPropertyName pname = objliteralProperty();
if (pname == null) {
propertyName = null;
reportError("msg.bad.prop");
Expand All @@ -3381,15 +3380,15 @@ private ObjectLiteral objectLiteral()
&& peeked != Token.RC)
{
if (peeked == Token.LP) {
entryKind = METHOD_ENTRY;
} else if (pname.getType() == Token.NAME) {
entryKind = PropertyEntryKind.METHOD;
} else if (!pname.computed && pname.name.getType() == Token.NAME) {
if ("get".equals(propertyName)) {
entryKind = GET_ENTRY;
entryKind = PropertyEntryKind.GET;
} else if ("set".equals(propertyName)) {
entryKind = SET_ENTRY;
entryKind = PropertyEntryKind.SET;
}
}
if (entryKind == GET_ENTRY || entryKind == SET_ENTRY) {
if (entryKind == PropertyEntryKind.GET || entryKind == PropertyEntryKind.SET) {
pname = objliteralProperty();
if (pname == null) {
reportError("msg.bad.prop");
Expand All @@ -3401,35 +3400,38 @@ private ObjectLiteral objectLiteral()
} else {
propertyName = ts.getString();
ObjectProperty objectProp = methodDefinition(
ppos, pname, entryKind);
pname.setJsDocNode(jsdocNode);
ppos, pname.name, entryKind);
pname.name.setJsDocNode(jsdocNode);
objectProp.setComputed(pname.computed);
elems.add(objectProp);
}
} else {
pname.setJsDocNode(jsdocNode);
elems.add(plainProperty(pname, tt));
pname.name.setJsDocNode(jsdocNode);
ObjectProperty objectProp = plainProperty(pname.name, tt);
objectProp.setComputed(pname.computed);
elems.add(objectProp);
}
}
}

if (this.inUseStrictDirective && propertyName != null) {
switch (entryKind) {
case PROP_ENTRY:
case METHOD_ENTRY:
case LITERAL:
case METHOD:
if (getterNames.contains(propertyName)
|| setterNames.contains(propertyName)) {
addError("msg.dup.obj.lit.prop.strict", propertyName);
}
getterNames.add(propertyName);
setterNames.add(propertyName);
break;
case GET_ENTRY:
case GET:
if (getterNames.contains(propertyName)) {
addError("msg.dup.obj.lit.prop.strict", propertyName);
}
getterNames.add(propertyName);
break;
case SET_ENTRY:
case SET:
if (setterNames.contains(propertyName)) {
addError("msg.dup.obj.lit.prop.strict", propertyName);
}
Expand Down Expand Up @@ -3458,34 +3460,54 @@ private ObjectLiteral objectLiteral()
return pn;
}

private AstNode objliteralProperty() throws IOException {
AstNode pname;
private static class ObjectPropertyName {
AstNode name;
boolean computed;

public ObjectPropertyName(AstNode name, boolean computed) {
this.name = name;
this.computed = computed;
}
}

private ObjectPropertyName objliteralProperty() throws IOException {
AstNode name;
boolean computed = false;
int tt = peekToken();
switch(tt) {
case Token.NAME:
pname = createNameNode();
name = createNameNode();
break;

case Token.STRING:
pname = createStringLiteral();
name = createStringLiteral();
break;

case Token.NUMBER:
pname = new NumberLiteral(
name = new NumberLiteral(
ts.tokenBeg, ts.getString(), ts.getNumber());
break;

case Token.LB:
if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
computed = true;
consumeToken();
name = assignExpr();
mustMatchToken(Token.RB, "msg.no.bracket.arg");
break;
}

default:
if (compilerEnv.isReservedKeywordAsIdentifier()
&& TokenStream.isKeyword(ts.getString())) {
// convert keyword to property name, e.g. ({if: 1})
pname = createNameNode();
name = createNameNode();
break;
}
return null;
}

return pname;
return new ObjectPropertyName(name, computed);
}

private ObjectProperty plainProperty(AstNode property, int ptt)
Expand All @@ -3512,7 +3534,7 @@ private ObjectProperty plainProperty(AstNode property, int ptt)
return pn;
}

private ObjectProperty methodDefinition(int pos, AstNode propName, int entryKind)
private ObjectProperty methodDefinition(int pos, AstNode propName, PropertyEntryKind entryKind)
throws IOException
{
FunctionNode fn = function(FunctionNode.FUNCTION_EXPRESSION);
Expand All @@ -3523,15 +3545,15 @@ private ObjectProperty methodDefinition(int pos, AstNode propName, int entryKind
}
ObjectProperty pn = new ObjectProperty(pos);
switch (entryKind) {
case GET_ENTRY:
case GET:
pn.setIsGetterMethod();
fn.setFunctionIsGetterMethod();
break;
case SET_ENTRY:
case SET:
pn.setIsSetterMethod();
fn.setFunctionIsSetterMethod();
break;
case METHOD_ENTRY:
case METHOD:
pn.setIsNormalMethod();
fn.setFunctionIsNormalMethod();
break;
Expand Down
9 changes: 9 additions & 0 deletions src/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,15 @@ static String toStringIdOrIndex(Context cx, Object id)
}
}

public static Object toPropertyKey(Object elem, Context cx) {
String s = toStringIdOrIndex(cx, elem);
if (s != null) {
return s;
}
return lastIndexResult(cx);
}


/**
* Call obj.[[Get]](id)
*
Expand Down
1 change: 1 addition & 0 deletions src/org/mozilla/javascript/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ public static String typeToName(int token) {
case COMMENT: return "COMMENT";
case GENEXPR: return "GENEXPR";
case METHOD: return "METHOD";
case ARROW: return "ARROW";
}

// Token without name
Expand Down
16 changes: 16 additions & 0 deletions src/org/mozilla/javascript/ast/ObjectProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ public boolean isMethod() {
return isGetterMethod() || isSetterMethod() || isNormalMethod();
}

public boolean isComputed() {
return computed;
}

public void setComputed(boolean computed) {
this.computed = computed;
}

@Override
public String toSource(int depth) {
StringBuilder sb = new StringBuilder();
Expand All @@ -112,11 +120,19 @@ public String toSource(int depth) {
} else if (isSetterMethod()) {
sb.append("set ");
}
if (computed) {
sb.append("[");
}
sb.append(left.toSource(getType()==Token.COLON ? 0 : depth));
if (computed) {
sb.append("]");
}
if (type == Token.COLON) {
sb.append(": ");
}
sb.append(right.toSource(getType()==Token.COLON ? 0 : depth+1));
return sb.toString();
}

private boolean computed;
}

0 comments on commit 0fef518

Please sign in to comment.