Skip to content

Commit

Permalink
non-spec global print
Browse files Browse the repository at this point in the history
  • Loading branch information
devsnek committed Oct 12, 2018
1 parent 4bd3ef7 commit e7b44d4
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 71 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,15 @@ const realm = new Realm({
// ensureCanCompileStrings() {},
});

const completion = realm.evaluateScript(`
realm.evaluateScript(`
'use strict';
1 + 1;
print(1 + 1); // prints "2" to the console
`);

console.log(completion.Value); // NumberValue { 2 }
```

`$ node --experimental-modules yourfile.mjs`

Check out `test/test262.mjs` for an example of a `print` function.

### How Completions Work

We run a source transform:
Expand Down
80 changes: 79 additions & 1 deletion src/api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { Value, Descriptor, Type } from './value.mjs';
import { ParseScript } from './parse.mjs';
import {
Q,
Completion,
NormalCompletion,
ThrowCompletion,
Expand Down Expand Up @@ -41,7 +42,23 @@ class APIRealm {
const global = Value.undefined;
const thisValue = Value.undefined;
SetRealmGlobalObject(realm, global, thisValue);
this.global = SetDefaultGlobalBindings(realm);
const globalObj = SetDefaultGlobalBindings(realm);
// Create any implementation-defined global object properties on globalObj.
globalObj.DefineOwnProperty(new Value('print'), Descriptor({
Value: CreateBuiltinFunction((args) => {
if (global.$262 && global.$262.handlePrint) {
global.$262.handlePrint(...args);
} else {
console.log(...args.map((a) => inspect(a))); // eslint-disable-line no-console
}
return Value.undefined;
}, [], realm),
Writable: Value.true,
Enumerable: Value.false,
Configurable: Value.true,
}));

this.global = globalObj;

surroundingAgent.executionContextStack.pop();

Expand Down Expand Up @@ -126,3 +143,64 @@ export {
APIValue as Value,
APIObject as Object,
};

export function inspect(value, realm = surroundingAgent.currentRealmRecord, quote = true, indent = 0) {
const type = Type(value);
if (type === 'Undefined') {
return 'undefined';
} else if (type === 'Null') {
return 'null';
} else if (type === 'String') {
return quote ? `'${value.stringValue()}'` : value.stringValue();
} else if (type === 'Number') {
return value.numberValue().toString();
} else if (type === 'Boolean') {
return value.value.toString();
} else if (type === 'Symbol') {
return `Symbol(${value.Description.stringValue()})`;
} else if (type === 'Object') {
if ('Call' in value) {
const name = value.properties.get(new Value('name'));
if (name !== undefined) {
return `[Function: ${name.Value.stringValue()}]`;
}
return '[Function: <anonymous>]';
}
const errorToString = realm.Intrinsics['%ErrorPrototype%'].properties.get(new Value('toString')).Value;
const toString = Q(AbstractOps.Get(value, new Value('toString')));
if (toString.nativeFunction === errorToString.nativeFunction) {
return Q(toString.Call(value, [])).stringValue();
}
try {
const keys = Q(value.OwnPropertyKeys());
if (keys.length === 0) {
return '{}';
}
const isArray = AbstractOps.IsArray(value) === Value.true;
let out = isArray ? '[' : '{';
indent += 1;
for (const key of keys) {
const C = value.properties.get(key);
out = `${out}\n${' '.repeat(indent)}${inspect(key, realm, false, indent)}: ${inspect(C.Value, realm, false, indent)},`;
}
indent -= 1;
return `${out}\n${' '.repeat(indent)}${isArray ? ']' : '}'}`;
} catch (e) {
const objectToString = realm.Intrinsics['%ObjProto_toString%'];
if (toString.nativeFunction === objectToString.nativeFunction) {
return Q(toString.Call(value, [])).stringValue();
} else {
const ctor = Q(AbstractOps.Get(value, new Value('constructor')));
if (Type(ctor) === 'Object') {
const ctorName = Q(AbstractOps.Get(ctor, new Value('name'))).stringValue();
if (ctorName !== '') {
return `#<${ctorName}>`;
}
return '[object Unknown]';
}
return '[object Unknown]';
}
}
}
throw new RangeError();
}
11 changes: 2 additions & 9 deletions src/engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,8 @@ import {
IsPropertyKey,
ToBoolean,
} from './abstract-ops/all.mjs';
import {
GlobalDeclarationInstantiation,
} from './runtime-semantics/all.mjs';
import {
Evaluate_Script,
} from './evaluator.mjs';
import { GlobalDeclarationInstantiation } from './runtime-semantics/all.mjs';
import { Evaluate_Script } from './evaluator.mjs';

export class Agent {
constructor() {
Expand Down Expand Up @@ -256,6 +252,3 @@ export function IsConcatSpreadable(O) {
}
return IsArray(O);
}

// 24.4.1.9 #sec-suspend
export function Suspend() {}
4 changes: 0 additions & 4 deletions src/realm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,12 @@ export function SetRealmGlobalObject(realmRec, globalObj, thisValue) {
const intrinsics = realmRec.Intrinsics;
globalObj = ObjectCreate(intrinsics['%ObjectPrototype%']);
}

if (Type(thisValue) === 'Undefined') {
thisValue = globalObj;
}

realmRec.GlobalObject = globalObj;

const newGlobalEnv = NewGlobalEnvironment(globalObj, thisValue);
realmRec.GlobalEnv = newGlobalEnv;

return realmRec;
}

Expand Down
54 changes: 3 additions & 51 deletions test/test262.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Abstract,
Completion,
AbruptCompletion,
inspect,
} from '../lib/api.mjs';

util.inspect.defaultOptions.depth = 2;
Expand Down Expand Up @@ -47,60 +48,11 @@ function X(val) {
return val;
}

function inspect(realm, value) {
const type = Abstract.Type(value);
if (type === 'Undefined') {
return 'undefined';
} else if (type === 'Null') {
return 'null';
} else if (type === 'String' || type === 'Number' || type === 'Boolean') {
return X(Abstract.ToString(value)).stringValue();
} else if (type === 'Symbol') {
return `Symbol(${value.Description.stringValue()})`;
} else if (type === 'Object') {
const funcToString = realm.realm.Intrinsics['%FunctionPrototype%'].properties.get(new Value(realm, 'toString')).Value;
const errorToString = realm.realm.Intrinsics['%ErrorPrototype%'].properties.get(new Value(realm, 'toString')).Value;
const objectToString = realm.realm.Intrinsics['%ObjProto_toString%'];
const toString = X(Abstract.Get(value, new Value(realm, 'toString')));
if (toString.nativeFunction === errorToString.nativeFunction
|| toString.nativeFunction === objectToString.nativeFunction
|| toString.nativeFunction === funcToString.nativeFunction) {
const s = X(toString.Call(value, [])).stringValue();
if (value.hostTrace) {
return `${s}\n${value.hostTrace}`;
}
return s;
} else {
const ctor = X(Abstract.Get(value, new Value(realm, 'constructor')));
if (Abstract.Type(ctor) === 'Object') {
const ctorName = X(Abstract.Get(ctor, new Value(realm, 'name'))).stringValue();
if (ctorName !== '') {
return `#<${ctorName}>`;
} else {
return '[object Unknown]';
}
} else {
return '[object Unknown]';
}
}
} else if (type === 'Completion') {
return inspect(realm, value.Value);
} else {
throw new RangeError();
}
}

function createRealm() {
const realm = new Realm();

const $262 = new APIObject(realm);

Abstract.CreateDataProperty(realm.global, new Value(realm, 'print'), new Value(realm, (args) => {
if ($262.handlePrint) {
$262.handlePrint(...args);
}
return new Value(realm, undefined);
}));
realm.global.$262 = $262;

Abstract.CreateDataProperty($262, new Value(realm, 'global'), realm.global);
Abstract.CreateDataProperty($262, new Value(realm, 'createRealm'), new Value(realm, () => createRealm()));
Expand Down Expand Up @@ -151,7 +103,7 @@ async function run({ source, meta, strict }) {
if (meta.negative) {
return { status: PASS };
} else {
return { status: FAIL, error: inspect($262.realm, completion) };
return { status: FAIL, error: inspect(completion.Value, $262.realm.realm) };
}
}

Expand Down

0 comments on commit e7b44d4

Please sign in to comment.