Skip to content

Instantly share code, notes, and snippets.

@devsnek
Created October 16, 2018 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devsnek/c0d9a583043cabc22a9ce3b2a96154a7 to your computer and use it in GitHub Desktop.
Save devsnek/c0d9a583043cabc22a9ce3b2a96154a7 to your computer and use it in GitHub Desktop.
diff --git a/src/abstract-ops/testing-comparison.mjs b/src/abstract-ops/testing-comparison.mjs
index c03de47..032d52f 100644
--- a/src/abstract-ops/testing-comparison.mjs
+++ b/src/abstract-ops/testing-comparison.mjs
@@ -38,6 +38,8 @@ export function RequireObjectCoercible(argument) {
return surroundingAgent.Throw('TypeError', 'undefined cannot be convered to an object');
case 'Null':
return surroundingAgent.Throw('TypeError', 'null cannot be converted to an object');
+ case 'ReferenceValue':
+ return surroundingAgent.Throw('TypeError', 'reference cannot be converted to an object');
case 'Boolean':
case 'Number':
case 'String':
diff --git a/src/abstract-ops/type-conversion.mjs b/src/abstract-ops/type-conversion.mjs
index 563e140..dd5f69b 100644
--- a/src/abstract-ops/type-conversion.mjs
+++ b/src/abstract-ops/type-conversion.mjs
@@ -49,9 +49,7 @@ export function ToPrimitive(input, PreferredType) {
}
// 7.1.1.1 #sec-ordinarytoprimitive
-export function OrdinaryToPrimitive(
- O, hint,
-) {
+export function OrdinaryToPrimitive(O, hint) {
Assert(Type(O) === 'Object');
Assert(Type(hint) === 'String'
&& (hint.stringValue() === 'string' || hint.stringValue() === 'number'));
@@ -75,6 +73,10 @@ export function OrdinaryToPrimitive(
// 7.1.2 #sec-toboolean
export function ToBoolean(argument) {
+ if (Type(argument) === 'ReferenceValue') {
+ return Value.true;
+ }
+
if (Type(argument) === 'Undefined') {
return Value.false;
}
@@ -131,6 +133,7 @@ export function ToNumber(argument) {
// FIXME(devsnek): https://tc39.github.io/ecma262/#sec-runtime-semantics-mv-s
return new Value(+(argument.stringValue()));
case 'Symbol':
+ case 'ReferenceValue':
return surroundingAgent.Throw('TypeError');
case 'Object': {
const primValue = Q(ToPrimitive(argument, 'Number'));
@@ -272,6 +275,7 @@ export function ToString(argument) {
return NumberToString(argument);
case 'String':
return argument;
+ case 'ReferenceValue':
case 'Symbol':
return surroundingAgent.Throw('TypeError');
case 'Object': {
@@ -310,6 +314,8 @@ export function ToObject(argument) {
return surroundingAgent.Throw('TypeError', 'cannot convert undefined to object');
case 'Null':
return surroundingAgent.Throw('TypeError', 'cannot convert null to object');
+ case 'ReferenceValue':
+ return surroundingAgent.Throw('TypeError', 'cannot convert reference to object');
case 'Boolean': {
const obj = ObjectCreate(surroundingAgent.intrinsic('%BooleanPrototype%'));
obj.BooleanData = argument;
diff --git a/src/api.mjs b/src/api.mjs
index 5e212ea..c3febad 100644
--- a/src/api.mjs
+++ b/src/api.mjs
@@ -145,7 +145,19 @@ export {
export function Inspect(value, realm = surroundingAgent.currentRealmRecord, quote = true, indent = 0) {
const type = Type(value);
- if (type === 'Undefined') {
+ if (type === 'ReferenceValue') {
+ const { Reference } = value;
+ const name = Inspect(Reference.ReferencedName, realm, false, 0);
+ let prefix = '';
+ if (Reference.BaseValue === realm.GlobalObject) {
+ prefix = '(global).';
+ } else if (Reference.BaseValue === Value.null) {
+ prefix = 'unresolved ';
+ } else if (Reference.BaseValue instanceof Value) {
+ prefix = '(intermediate value).';
+ }
+ return `[reference -> ${prefix}${name}]`;
+ } else if (type === 'Undefined') {
return 'undefined';
} else if (type === 'Null') {
return 'null';
@@ -202,5 +214,5 @@ export function Inspect(value, realm = surroundingAgent.currentRealmRecord, quot
}
}
}
- throw new RangeError();
+ throw new RangeError(type);
}
diff --git a/src/parse.mjs b/src/parse.mjs
index e088af7..367a1d8 100644
--- a/src/parse.mjs
+++ b/src/parse.mjs
@@ -4,6 +4,59 @@ const Parser = acorn.Parser.extend((P) => class Parse262 extends P {
constructor(options, source) {
super({ ...options, ecmaVersion: 2019 }, source);
}
+
+ parseExprAtom(refDestructuringErrors) {
+ if (this.type === acorn.tokTypes.star) {
+ // UnaryExpression : `*` UnaryExpression
+ this.eat(acorn.tokTypes.star);
+ const node = this.startNode();
+ node.operator = '*';
+ node.prefix = true;
+ node.argument = this.parseMaybeUnary(refDestructuringErrors);
+ return this.finishNode(node, 'UnaryExpression');
+ } else if (this.type === acorn.tokTypes.starstar) {
+ this.eat(acorn.tokTypes.starstar);
+ const outer = this.startNode();
+ outer.operator = '*';
+
+ const inner = this.startNode();
+ inner.operator = '*';
+ inner.prefix = true;
+ inner.argument = this.parseMaybeUnary(refDestructuringErrors);
+
+ outer.argument = this.finishNode(inner, 'UnaryExpression');
+
+ return this.finishNode(outer, 'UnaryExpression');
+ } else if (this.type === acorn.tokTypes.bitwiseAND) {
+ // UnaryExpression :
+ // `&` MemberExpression
+ // `&` Identifier
+ this.eat(acorn.tokTypes.bitwiseAND);
+ const node = this.startNode();
+ node.operator = '&';
+ const startPos = this.start;
+ const startLoc = this.startLoc;
+ let base = this.parseIdent(false);
+ while (true) {
+ const computed = this.eat(acorn.tokTypes.bracketL);
+ if (!computed && !this.eat(acorn.tokTypes.dot)) {
+ break;
+ }
+ const inner = this.startNodeAt(startPos, startLoc);
+ inner.object = base;
+ inner.property = computed ? this.parseExpression() : this.parseIdent(true);
+ inner.computed = computed;
+ if (computed) {
+ this.expect(acorn.tokTypes.bracketR);
+ }
+ base = this.finishNode(inner, 'MemberExpression');
+ }
+ this.semicolon();
+ node.argument = base;
+ return this.finishNode(node, 'UnaryExpression');
+ }
+ return super.parseExprAtom(refDestructuringErrors);
+ }
});
function deepFreeze(obj) {
diff --git a/src/runtime-semantics/MemberExpression.mjs b/src/runtime-semantics/MemberExpression.mjs
index c746cb0..3f06f13 100644
--- a/src/runtime-semantics/MemberExpression.mjs
+++ b/src/runtime-semantics/MemberExpression.mjs
@@ -47,7 +47,7 @@ function* Evaluate_MemberExpression_IdentifierName(MemberExpression, IdentifierN
// #sec-property-accessors-runtime-semantics-evaluation
// MemberExpression :
// MemberExpression `[` Expression `]`
-// MemberEXpression `.` IdentifierName
+// MemberExpression `.` IdentifierName
// CallExpression :
// CallExpression `[` Expression `]`
// CallExpression `.` IdentifierName
diff --git a/src/runtime-semantics/UnaryExpression.mjs b/src/runtime-semantics/UnaryExpression.mjs
index 1f29657..42a5ce2 100644
--- a/src/runtime-semantics/UnaryExpression.mjs
+++ b/src/runtime-semantics/UnaryExpression.mjs
@@ -25,7 +25,7 @@ import {
} from '../abstract-ops/all.mjs';
import { Evaluate_Expression } from '../evaluator.mjs';
import { Q, ReturnIfAbrupt, X } from '../completion.mjs';
-import { Type, Value } from '../value.mjs';
+import { Type, Value, ReferenceValue } from '../value.mjs';
import { outOfRange } from '../helpers.mjs';
// #sec-delete-operator-runtime-semantics-evaluation
@@ -80,6 +80,8 @@ function* Evaluate_UnaryExpression_Typeof(UnaryExpression) {
const type = Type(val);
switch (type) {
+ case 'Reference':
+ return new Value('reference');
case 'Undefined':
return new Value('undefined');
case 'Null':
@@ -160,6 +162,25 @@ export function* Evaluate_UnaryExpression(UnaryExpression) {
case isUnaryExpressionWithBang(UnaryExpression):
return yield* Evaluate_UnaryExpression_Bang(UnaryExpression.argument);
+ case UnaryExpression.operator === '*': {
+ const expr = yield* Evaluate_Expression(UnaryExpression.argument);
+ const value = Q(GetValue(expr));
+ if (Type(value) !== 'ReferenceValue') {
+ return surroundingAgent.Throw('TypeError', 'Only references can be dereferenced');
+ }
+ return Q(GetValue(value.Reference));
+ }
+
+ case UnaryExpression.operator === '&': {
+ const ref = yield* Evaluate_Expression(UnaryExpression.argument);
+ ReturnIfAbrupt(ref);
+ Assert(Type(ref) === 'Reference');
+ if (!(ref.BaseValue instanceof Value)) {
+ Q(GetValue(ref));
+ }
+ return new ReferenceValue(ref);
+ }
+
default:
throw outOfRange('Evaluate_UnaryExpression', UnaryExpression);
}
diff --git a/src/value.mjs b/src/value.mjs
index 4f42350..3734655 100644
--- a/src/value.mjs
+++ b/src/value.mjs
@@ -68,6 +68,13 @@ export function Value(value) {
export class PrimitiveValue extends Value {}
+export class ReferenceValue extends Value {
+ constructor(ref) {
+ super();
+ this.Reference = ref;
+ }
+}
+
export class UndefinedValue extends PrimitiveValue {}
export class NullValue extends PrimitiveValue {}
@@ -974,11 +981,7 @@ export class ProxyExoticObjectValue extends ObjectValue {
}
export class Reference {
- constructor(
- BaseValue,
- ReferencedName,
- StrictReference,
- ) {
+ constructor(BaseValue, ReferencedName, StrictReference) {
this.BaseValue = BaseValue;
this.ReferencedName = ReferencedName;
this.StrictReference = StrictReference;
@@ -1026,6 +1029,10 @@ export class DataBlock extends Uint8Array {
}
export function Type(val) {
+ if (val instanceof ReferenceValue) {
+ return 'ReferenceValue';
+ }
+
if (val instanceof UndefinedValue) {
return 'Undefined';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment