Skip to content

Commit

Permalink
feat: support generator, add eval5 benchmark, improve reporter (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
XGHeaven authored Jun 12, 2024
1 parent 3a57c1f commit 71014ab
Show file tree
Hide file tree
Showing 24 changed files with 1,010 additions and 521 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
# branches:
# - main
jobs:
Test262:
Benchmark:
runs-on: ubuntu-latest
environment: Benchmark
steps:
Expand Down
2 changes: 1 addition & 1 deletion benchmark/cases/v8-7/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
// import './navier-stokes.js';

var success = true
print = typeof console == 'object' && typeof console.log == 'function' ? console.log : print
var print = typeof console == 'object' && typeof console.log == 'function' ? console.log : print

function PrintResult(name, result) {
print(name + ': ' + result)
Expand Down
8 changes: 8 additions & 0 deletions benchmark/run-eval5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { Interpreter } = require('eval5')
const fs = require('fs')
const path = require('path')

const interpreter = new Interpreter(global, {})

const script = fs.readFileSync(path.join(__dirname, 'cases/v8-7/_bundle.js'), 'utf8')
interpreter.evaluate(script)
8 changes: 8 additions & 0 deletions benchmark/run-sable.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { spawnSync } = require('child_process')
const os = require('os')
const fs = require('fs')
const path = require('path')
const VM = require('sablejs/runtime')
const vm = new (VM())()

Expand Down Expand Up @@ -29,6 +30,13 @@ function compile(codestr) {
}

;(async () => {
const sablejsAggrement = path.join(require.resolve('sablejs/bin/sablejs'), '../.aggrement')
if (!fs.existsSync(sablejsAggrement)) {
console.log('Write agreement')
fs.writeFileSync(sablejsAggrement, 'y', 'utf8')
} else {
console.log('.aggrement already agreed')
}
let codestr = fs.readFileSync(__dirname + '/cases/v8-7/_bundle.js').toString()
codestr = await compile(codestr)
vm.run(codestr, true)
Expand Down
3 changes: 3 additions & 0 deletions benchmark/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ echo "Run jsscript"

echo "Run sablejs"
node ./benchmark/run-sable.js

echo "Run eval5"
node ./benchmark/run-eval5.js
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"devDependencies": {
"@types/node": "^20.10.4",
"@types/yargs": "^17.0.32",
"eval5": "^1.4.7",
"prettier": "^3.1.1",
"sablejs": "^1.1.0",
"test262-harness": "^9.2.0",
Expand Down
39 changes: 39 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 45 additions & 1 deletion scripts/analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,55 @@ const fs = require('fs')

const result = require('../.result/result.json')

const allTests = result.length
const successGroup = {}
const expectedGroup = {}
const allGroup = {}
const tree = {}

const expectedErrorMessages = [`'eval' is not defined`]

const allErrorMessages = new Set()

function applyToTree(node, path, info) {
if (info.result.pass) {
node._pass = (node._pass || 0) + 1
} else {
node._fail = (node._fail || 0) + 1
}
node._total = (node._total || 0) + 1
if (path.length === 0) {
node._info = [...(node._info || []), info]
return
}
const [head, ...tail] = path
if (!node[head]) {
node[head] = {}
}
applyToTree(node[head], tail, info)
}

function dumpTree(node, depth = 0, contents = []) {
const indent = ' '.repeat(depth)
for (const name of Object.keys(node)) {
if (name.startsWith('_')) {
continue
}
const pass = node[name]._pass || 0
const total = node[name]._total || 0
const percent = parseInt((pass / total) * 100)
contents.push(indent + '- <details>')
contents.push(indent + ` <summary>${name} ${percent}% (${pass}/${total})</summary>`)
contents.push('')
const child = node[name]
dumpTree(child, depth + 1, contents)
contents.push('')
contents.push(indent + ' </details>')
}
}

for (const r of result) {
const paths = r.relative.split('/')
applyToTree(tree, paths, r)
const { scenario } = r
if (!allGroup[scenario]) {
allGroup[scenario] = 1
Expand Down Expand Up @@ -69,3 +108,8 @@ for (const scenario of Object.keys(allGroup)) {
JSON.stringify(makeBadge(scenario, allGroup[scenario], successGroup[scenario] || 0, expectedGroup[scenario] || 0))
)
}

const contents = []
dumpTree(tree, 0, contents)
const md = contents.join('\n')
fs.writeFileSync('./.result/reporter.md', md, 'utf8')
17 changes: 17 additions & 0 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,20 @@ export function jsArrayLoop(

return Result.SUCCESS
}

export function JSArrayToList(ctx: Context, array: JSObjectValue): JSValue[] | null {
const values: JSValue[] = []
const length = JSGetLengthOfArrayLike(ctx, array)
if (length === -1) {
return null
}
const ret = jsArrayLoop(ctx, array, 0, length, (value) => {
values.push(value)
return Result.SUCCESS
})

if (ret === Result.ERROR) {
return null
}
return values
}
65 changes: 65 additions & 0 deletions src/async-function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { JSClassCall } from './class'
import { Context } from './context'
import { callFrame } from './executor'
import { StackFrame, createStackFrame } from './frame'
import { JSNewObjectFromCtor, JSObjectType, setObjectData } from './object'
import { JSValue, JS_UNDEFINED, isExceptionValue } from './value'

export const JS_CALL_ASYNC_YIELD = 1
export const JS_CALL_ASYNC_YIELD_STAR = 2
export const JS_CALL_ASYNC_RETURN = 3
export const JS_CALL_ASYNC_AWAIT = 4

export interface JSAsyncFunctionData {}

export const enum JSGeneratorState {
SUSPENDED_START,
SUSPENDED_YIELD,
SUSPENDED_YIELD_STAR,
EXECUTING,
COMPLETED,
}

export interface JSGeneratorData {
funcState: JSAsyncLikeFunctionState
state: JSGeneratorState
}

export interface JSAsyncLikeFunctionState {
thisVal: JSValue
throwFlag: boolean
frame: StackFrame
}

export const generatorCallHandler: JSClassCall = (ctx, fnObj, thisObj, args, isNew) => {
const data: JSGeneratorData = {
state: JSGeneratorState.SUSPENDED_START,
funcState: {
thisVal: thisObj,
throwFlag: false,
frame: createStackFrame(ctx, fnObj, args),
},
}

// TODO: add initial step

const obj = JSNewObjectFromCtor(ctx, fnObj, JSObjectType.Generator)
if (isExceptionValue(obj)) {
return obj
}
setObjectData(obj.value, data)

return obj
}

export function JSAsyncLikeFunctionResume(ctx: Context, data: JSAsyncLikeFunctionState): JSValue {
return callFrame(ctx, data.frame, data.thisVal, JS_UNDEFINED, data.throwFlag)
}

export function freeGeneratorData(ctx: Context, data: JSGeneratorData) {
if (data.state === JSGeneratorState.COMPLETED) {
return
}
data.state = JSGeneratorState.COMPLETED
// TODO: remove funcState
}
18 changes: 15 additions & 3 deletions src/builtin/Function.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { JSArrayToList } from '../array'
import { Context } from '../context'
import { JSThrowTypeError } from '../error'
import { callInternal } from '../executor'
import { HostFunction } from '../function'
import { JS_UNDEFINED } from '../value'
import { JSValue, JS_UNDEFINED, isNullValue, isObjectValue, isUndefinedValue } from '../value'
import { JSApplyPropertyDefinitions, PropertyDefinitions, defHostFunction, defHostValueConfigurable } from './helper'

const fnProtoApply: HostFunction = (ctx, thisVal, args) => {
return JSThrowTypeError(ctx, 'Unsupported apply')
const fnProtoApply: HostFunction = (ctx, thisVal, [thisArg, args]) => {
let callArgs: JSValue[] | null = []
if (!args || isUndefinedValue(args) || isNullValue(args)) {
callArgs = []
} else if (isObjectValue(args)) {
callArgs = JSArrayToList(ctx, args)
if (!callArgs) {
return JSThrowTypeError(ctx, 'args is not array like')
}
} else {
return JSThrowTypeError(ctx, 'args is not array object like')
}
return callInternal(ctx, thisVal, thisArg, JS_UNDEFINED, callArgs)
}

const fnProtoCall: HostFunction = (ctx, thisVal, args) => {
Expand Down
Loading

0 comments on commit 71014ab

Please sign in to comment.