Skip to content

Commit

Permalink
[Update] Implement object expression
Browse files Browse the repository at this point in the history
  • Loading branch information
Siubaak committed Jun 11, 2019
1 parent f703ef8 commit 417b787
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 21 deletions.
61 changes: 49 additions & 12 deletions src_/compile/expression.ts
Expand Up @@ -17,6 +17,28 @@ export function ArrayExpression(node: estree.ArrayExpression, state: State) {
}

export function ObjectExpression(node: estree.ObjectExpression, state: State) {
const propKinds = []
for (let i = 0; i < node.properties.length; i++) {
const property = node.properties[i]
if (property.type as any === 'SpreadElement') {

} else {
// key
const propKey = property.key
if (propKey.type === 'Identifier') {
state.opCodes.push({ op: OP.LOADK, val: propKey.name })
} else if (propKey.type === 'Literal') {
state.opCodes.push({ op: OP.LOADK, val: propKey.value })
} else {
// property.computed === true
compile(propKey, state)
}
// value
compile(property.value, state)
propKinds.push(property.kind)
}
}
state.opCodes.push({ op: OP.OBJ, val: propKinds })
}

export function FunctionExpression(node: estree.FunctionExpression, state: State) {
Expand Down Expand Up @@ -52,15 +74,28 @@ export function BinaryExpression(node: estree.BinaryExpression, state: State) {

export function AssignmentExpression(node: estree.AssignmentExpression, state: State) {
compile(node.right, state)
if (node.left.type === 'Identifier') {
const symbol = state.symbols.get(node.left.name)
state.opCodes.push({ op: OP.COPY })
const left = node.left
if (left.type === 'Identifier') {
const symbol = state.symbols.get(left.name)
if (symbol.type === 'const') throw new TypeError('Assignment to constant variable')
const binaryOp = node.operator.substring(0, node.operator.length - 1)
if (binaryOp) {
state.opCodes.push({ op: OP.LOADV, val: symbol.pointer })
state.opCodes.push({ op: OP.BIOP, val: binaryOp })
}
state.opCodes.push({ op: OP.MOVE, val: symbol.pointer })
} else if (left.type === 'MemberExpression') {
compile(left.object, state)
const property = left.property
if (property.type === 'Identifier') {
state.opCodes.push({ op: OP.LOADK, val: property.name })
} else { // node.computed === true
compile(property, state)
}
state.opCodes.push({ op: OP.MSET })
} else {

}
}

Expand All @@ -72,12 +107,13 @@ export function LogicalExpression(node: estree.LogicalExpression, state: State)

export function MemberExpression(node: estree.MemberExpression, state: State) {
compile(node.object, state)
if (node.computed) {
compile(node.property, state)
} else {
state.opCodes.push({ op: OP.LOADK, val: (node.property as estree.Identifier).name })
const property = node.property
if (property.type === 'Identifier') {
state.opCodes.push({ op: OP.LOADK, val: property.name })
} else { // node.computed === true
compile(property, state)
}
state.opCodes.push({ op: OP.MEMB })
state.opCodes.push({ op: OP.MGET })
}

export function ConditionalExpression(node: estree.ConditionalExpression, state: State) {
Expand Down Expand Up @@ -107,12 +143,13 @@ export function CallExpression(node: estree.CallExpression, state: State) {
if (callee.type === 'MemberExpression') {
compile(callee.object, state)
state.opCodes.push({ op: OP.COPY })
if (callee.computed) {
compile(callee.property, state)
} else {
state.opCodes.push({ op: OP.LOADK, val: (callee.property as estree.Identifier).name })
const property = callee.property
if (property.type === 'Identifier') {
state.opCodes.push({ op: OP.LOADK, val: property.name })
} else { // node.computed === true
compile(property, state)
}
state.opCodes.push({ op: OP.MEMB })
state.opCodes.push({ op: OP.MGET })
} else {
state.opCodes.push({ op: OP.LOADV, val: state.symbols.get('this').pointer })
compile(callee, state)
Expand Down
6 changes: 5 additions & 1 deletion src_/compile/statement.ts
Expand Up @@ -20,7 +20,11 @@ export function DebuggerStatement(node: estree.DebuggerStatement, state: State)
}

export function ReturnStatement(node: estree.ReturnStatement, state: State) {
compile(node.argument, state)
if (node.argument) {
compile(node.argument, state)
} else {
state.opCodes.push({ op: OP.LOADK }) // load undefined into stack
}
state.opCodes.push({ op: OP.RET })
}

Expand Down
54 changes: 50 additions & 4 deletions src_/jsvm/index.ts
@@ -1,5 +1,6 @@
import State from '../state'
import { OP, SIGNAL } from '../share/const'
import { getDptor, define } from '../share/utils'

function step(state: State) {
const stack = state.stack
Expand Down Expand Up @@ -62,10 +63,50 @@ function step(state: State) {
case OP.IF: stack.pop() && (state.pc = code.val - 1); break
case OP.IFNOT: !stack.pop() && (state.pc = code.val - 1); break
case OP.ARR: stack.push(stack.splice(stack.length - code.val)); break
case OP.MEMB: {
case OP.OBJ: {
const object: any = {}
const propKinds = code.val
const keyValue = stack.splice(stack.length - propKinds.length * 2)
for (let i = 0; i < propKinds.length * 2; i += 2) {
const key = keyValue[i]
const value = keyValue[i + 1]
const kind = propKinds[i / 2]
if (kind === 'init') {
object[key] = value
} else if (kind === 'get') {
const oriDptor = getDptor(object, key)
define(object, key, {
get: value,
set: oriDptor && oriDptor.set,
enumerable: true,
configurable: true
})
} else { // kind === 'set'
const oriDptor = getDptor(object, key)
define(object, key, {
get: oriDptor && oriDptor.get,
set: value,
enumerable: true,
configurable: true
})
}
}
stack.push(object)
break
}
case OP.MGET: {
const key = stack.pop()
const object = stack.pop()
stack.push(object[key])
const dptor = getDptor(object, key)
// if getter, just call
dptor && dptor.get ? object[key] : stack.push(object[key])
break
}
case OP.MSET: {
const key = stack.pop()
const object = stack.pop()
const value = stack.pop()
object[key] = value
break
}
case OP.FUNC: {
Expand All @@ -87,10 +128,15 @@ function step(state: State) {
state.pc = beginPc // offset pc to the function op codes
state.context = lexicalCtx // set the context as the lexical context of function

let s = SIGNAL.NONE
while (state.pc < endPc) {
if (step(state) === SIGNAL.RET) break
s = step(state)
if (s === SIGNAL.RET) break
}

if (s !== SIGNAL.RET) {
stack.push(undefined)
}

state.pc = resetPc // reset to the current pc
state.context = resetCtx // reset to the current context
})
Expand Down
5 changes: 3 additions & 2 deletions src_/share/const.ts
Expand Up @@ -8,8 +8,9 @@ export enum OP {
IF, // jump to specified position if true (val: jumped pc)
IFNOT, // jump to specified position if true (val: jumped pc)
ARR, // create array (val: number of items)
// DEF, // define properties of object (val: property key)
MEMB, // get member of object (no val)
OBJ, // create object (val: array of property kinds)
MGET, // get member of object (no val)
MSET, // set member of object (no val)
FUNC, // declare a function (val: end pc of its op codes)
CALL, // invoke functions or methods (val: number of parameters)
BRK, // break (val: label)
Expand Down
4 changes: 4 additions & 0 deletions src_/share/utils.ts
@@ -1,3 +1,7 @@
export const define = Object.defineProperty

export const getDptor = Object.getOwnPropertyDescriptor

export const getOwnNames = Object.getOwnPropertyNames

export function createSymbol(key: string) {
Expand Down
4 changes: 2 additions & 2 deletions src_/test.ts
Expand Up @@ -3,6 +3,6 @@ import Sval from '.'
const i = new Sval()

i.run(`
const j = function () {console.log(this)}
j.call([0,1])
var a = { get b() { return 1 } }
console.log(a.b)
`)

0 comments on commit 417b787

Please sign in to comment.