mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-17 17:01:37 +00:00
Initial static arrays of basic element types; Fixed member names in generic contexts
This commit is contained in:
116
src/compiler.ts
116
src/compiler.ts
@ -119,6 +119,13 @@ import {
|
||||
typesToNativeTypes
|
||||
} from "./types";
|
||||
|
||||
import {
|
||||
writeI32,
|
||||
writeI64,
|
||||
writeF32,
|
||||
writeF64
|
||||
} from "./util";
|
||||
|
||||
/** Compilation target. */
|
||||
export enum Target {
|
||||
/** WebAssembly with 32-bit pointers. */
|
||||
@ -4564,11 +4571,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let classType = contextualType.classType;
|
||||
if (
|
||||
classType &&
|
||||
classType == this.program.elementsLookup.get("Array") &&
|
||||
classType.typeArguments && classType.typeArguments.length == 1
|
||||
classType.prototype == this.program.elementsLookup.get("Array")
|
||||
) {
|
||||
return this.compileStaticArray(
|
||||
classType.typeArguments[0],
|
||||
assert(classType.typeArguments)[0],
|
||||
(<ArrayLiteralExpression>expression).elementExpressions,
|
||||
expression
|
||||
);
|
||||
@ -4725,29 +4731,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileStaticArray(elementType: Type, expressions: (Expression | null)[], reportNode: Node): ExpressionRef {
|
||||
// compile as static if all element expressions are precomputable, otherwise
|
||||
// initialize in place.
|
||||
var isStatic = true;
|
||||
var size = expressions.length;
|
||||
|
||||
var module = this.module;
|
||||
|
||||
// obtain the array type
|
||||
var arrayPrototype = assert(this.program.elementsLookup.get("Array"));
|
||||
if (!arrayPrototype || arrayPrototype.kind != ElementKind.CLASS_PROTOTYPE) return module.createUnreachable();
|
||||
var arrayType = (<ClassPrototype>arrayPrototype).resolve([ elementType ]);
|
||||
if (!arrayType) return module.createUnreachable();
|
||||
|
||||
var elementSize = expressions.length;
|
||||
var nativeType = elementType.toNativeType();
|
||||
var values: usize;
|
||||
var memorySize: usize;
|
||||
switch (nativeType) {
|
||||
case NativeType.I32: {
|
||||
values = changetype<usize>(new Int32Array(size));
|
||||
values = changetype<usize>(new Int32Array(elementSize));
|
||||
memorySize = elementSize * 4;
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
values = changetype<usize>(new Array<I64>(size));
|
||||
values = changetype<usize>(new Array<I64>(elementSize));
|
||||
memorySize = elementSize * 8;
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
values = changetype<usize>(new Float32Array(size));
|
||||
values = changetype<usize>(new Float32Array(elementSize));
|
||||
memorySize = elementSize * 4;
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
values = changetype<usize>(new Float64Array(size));
|
||||
values = changetype<usize>(new Float64Array(elementSize));
|
||||
memorySize = elementSize * 8;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -4760,9 +4775,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
var exprs = new Array<ExpressionRef>(size);
|
||||
// precompute value expressions
|
||||
var exprs = new Array<ExpressionRef>(elementSize);
|
||||
var expr: BinaryenExpressionRef;
|
||||
for (let i = 0; i < size; ++i) {
|
||||
for (let i = 0; i < elementSize; ++i) {
|
||||
exprs[i] = expressions[i]
|
||||
? this.compileExpression(<Expression>expressions[i], elementType)
|
||||
: elementType.toNativeZero(module);
|
||||
@ -4801,14 +4817,78 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
var usizeTypeSize = this.options.usizeType.byteSize;
|
||||
var headerSize = usizeTypeSize + 4 + 4; // memory + capacity + length
|
||||
|
||||
if (isStatic) {
|
||||
// TODO: convert to Uint8Array and create the segment
|
||||
let buffer = new Uint8Array(headerSize + memorySize);
|
||||
let segment = this.addMemorySegment(buffer);
|
||||
|
||||
// make header
|
||||
let offset = 0;
|
||||
if (usizeTypeSize == 8) {
|
||||
writeI64(i64_add(segment.offset, i64_new(headerSize)), buffer, 0); // memory
|
||||
} else {
|
||||
assert(i64_high(segment.offset) == 0);
|
||||
writeI32(i64_low(segment.offset) + headerSize, buffer, 0); // memory
|
||||
}
|
||||
offset += usizeTypeSize;
|
||||
writeI32(elementSize, buffer, offset); // capacity
|
||||
offset += 4;
|
||||
writeI32(elementSize, buffer, offset); // length
|
||||
offset += 4;
|
||||
assert(offset == headerSize);
|
||||
|
||||
// make memory
|
||||
switch (nativeType) {
|
||||
case NativeType.I32: {
|
||||
for (let i = 0; i < elementSize; ++i) {
|
||||
writeI32(changetype<i32[]>(values)[i], buffer, offset); offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
for (let i = 0; i < elementSize; ++i) {
|
||||
writeI64(changetype<I64[]>(values)[i], buffer, offset); offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
for (let i = 0; i < elementSize; ++i) {
|
||||
writeF32(changetype<f32[]>(values)[i], buffer, offset); offset += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
for (let i = 0; i < elementSize; ++i) {
|
||||
writeF64(changetype<f64[]>(values)[i], buffer, offset); offset += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
reportNode.range
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
}
|
||||
assert(offset == headerSize + memorySize);
|
||||
this.currentType = arrayType.type;
|
||||
return usizeTypeSize == 8
|
||||
? module.createI64(
|
||||
i64_low(segment.offset),
|
||||
i64_high(segment.offset)
|
||||
)
|
||||
: module.createI32(
|
||||
i64_low(segment.offset)
|
||||
);
|
||||
} else {
|
||||
// TODO: initialize in place
|
||||
// TODO: static elements *could* go into data segments while dynamic ones are initialized
|
||||
// on top? any benefits?
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
// TODO: alternatively, static elements could go into data segments while
|
||||
// dynamic ones are initialized on top? any benefits? (doesn't seem so)
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
compileNewExpression(expression: NewExpression, contextualType: Type): ExpressionRef {
|
||||
|
6
src/glue/js/float.d.ts
vendored
Normal file
6
src/glue/js/float.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/** @module glue/js *//***/
|
||||
|
||||
declare function f32_as_i32(value: f32): i32;
|
||||
declare function i32_as_f32(value: i32): f32;
|
||||
declare function f64_as_i64(value: f64): I64;
|
||||
declare function i64_as_f64(value: I64): f64;
|
24
src/glue/js/float.js
Normal file
24
src/glue/js/float.js
Normal file
@ -0,0 +1,24 @@
|
||||
const F64 = new Float64Array(1);
|
||||
const F32 = new Float32Array(F64.buffer);
|
||||
const I32 = new Int32Array(F64.buffer);
|
||||
|
||||
global.f32_as_i32 = function(value) {
|
||||
F32[0] = value;
|
||||
return I32[0];
|
||||
};
|
||||
|
||||
global.i32_as_f32 = function(value) {
|
||||
I32[0] = value;
|
||||
return F32[0];
|
||||
};
|
||||
|
||||
global.f64_as_i64 = function(value) {
|
||||
F64[0] = value;
|
||||
return i64_new(I32[0], I32[1]);
|
||||
};
|
||||
|
||||
global.i64_as_f64 = function(value) {
|
||||
I32[0] = i64_low(value);
|
||||
I32[1] = i64_high(value);
|
||||
return F64[0];
|
||||
};
|
@ -7,3 +7,4 @@
|
||||
import "../../../std/portable";
|
||||
import "./binaryen";
|
||||
import "./i64";
|
||||
import "./float";
|
||||
|
19
src/glue/wasm/float.ts
Normal file
19
src/glue/wasm/float.ts
Normal file
@ -0,0 +1,19 @@
|
||||
@global
|
||||
function f32_as_i32(value: f32): i32 {
|
||||
return reinterpret<i32>(value);
|
||||
}
|
||||
|
||||
@global
|
||||
function i32_as_f32(value: i32): f32 {
|
||||
return reinterpret<f32>(value);
|
||||
}
|
||||
|
||||
@global
|
||||
function f64_as_i64(value: f64): i64 {
|
||||
return reinterpret<i64>(value);
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_as_f64(value: i64): f64 {
|
||||
return reinterpret<f64>(value);
|
||||
}
|
@ -5,3 +5,4 @@
|
||||
*//***/
|
||||
|
||||
import "./i64";
|
||||
import "./float";
|
||||
|
@ -517,7 +517,11 @@ export class Program extends DiagnosticEmitter {
|
||||
classPrototype.members = new Map();
|
||||
}
|
||||
let staticField = new Global(
|
||||
this, name, internalName, declaration, Type.void
|
||||
this,
|
||||
name,
|
||||
internalName,
|
||||
declaration,
|
||||
Type.void
|
||||
);
|
||||
classPrototype.members.set(name, staticField);
|
||||
this.elementsLookup.set(internalName, staticField);
|
||||
@ -537,7 +541,8 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
let instanceField = new FieldPrototype(
|
||||
classPrototype,
|
||||
name, internalName,
|
||||
name,
|
||||
internalName,
|
||||
declaration
|
||||
);
|
||||
classPrototype.instanceMembers.set(name, instanceField);
|
||||
@ -2927,7 +2932,11 @@ export class ClassPrototype extends Element {
|
||||
instance.contextualTypeArguments
|
||||
);
|
||||
if (fieldType) {
|
||||
let fieldInstance = new Field(<FieldPrototype>member, (<FieldPrototype>member).internalName, fieldType);
|
||||
let fieldInstance = new Field(
|
||||
<FieldPrototype>member,
|
||||
internalName + INSTANCE_DELIMITER + (<FieldPrototype>member).simpleName,
|
||||
fieldType
|
||||
);
|
||||
switch (fieldType.byteSize) { // align
|
||||
case 1: break;
|
||||
case 2: {
|
||||
@ -2954,30 +2963,50 @@ export class ClassPrototype extends Element {
|
||||
if (!instance.members) instance.members = new Map();
|
||||
let methodPrototype = (<FunctionPrototype>member).resolvePartial(typeArguments); // reports
|
||||
if (methodPrototype) {
|
||||
methodPrototype.internalName = internalName + INSTANCE_DELIMITER + methodPrototype.simpleName;
|
||||
instance.members.set(member.simpleName, methodPrototype);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: { // instance properties are cloned with partially resolved getters and setters
|
||||
if (!instance.members) instance.members = new Map();
|
||||
assert((<Property>member).getterPrototype);
|
||||
let instanceProperty = new Property(this.program, member.simpleName, member.internalName, this);
|
||||
instanceProperty.getterPrototype = (
|
||||
let getterPrototype = assert((<Property>member).getterPrototype);
|
||||
let setterPrototype = (<Property>member).setterPrototype;
|
||||
let instanceProperty = new Property(
|
||||
this.program,
|
||||
member.simpleName,
|
||||
internalName + INSTANCE_DELIMITER + member.simpleName,
|
||||
this
|
||||
);
|
||||
let partialGetterPrototype = (
|
||||
(<FunctionPrototype>(<Property>member).getterPrototype).resolvePartial(
|
||||
typeArguments
|
||||
)
|
||||
);
|
||||
if ((<Property>member).setterPrototype) {
|
||||
instanceProperty.setterPrototype = (
|
||||
if (!partialGetterPrototype) return null;
|
||||
partialGetterPrototype.internalName = (
|
||||
internalName + INSTANCE_DELIMITER + partialGetterPrototype.simpleName
|
||||
);
|
||||
instanceProperty.getterPrototype = partialGetterPrototype;
|
||||
if (setterPrototype) {
|
||||
let partialSetterPrototype = (
|
||||
(<FunctionPrototype>(<Property>member).setterPrototype).resolvePartial(
|
||||
typeArguments
|
||||
)
|
||||
);
|
||||
if (!partialSetterPrototype) return null;
|
||||
partialSetterPrototype.internalName = (
|
||||
internalName + INSTANCE_DELIMITER + partialSetterPrototype.simpleName
|
||||
);
|
||||
instanceProperty.setterPrototype = partialSetterPrototype;
|
||||
}
|
||||
instance.members.set(member.simpleName, instanceProperty);
|
||||
break;
|
||||
}
|
||||
default: throw new Error("instance member expected");
|
||||
default: {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
src/util/binary.ts
Normal file
23
src/util/binary.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/** @module util *//***/
|
||||
|
||||
export function writeI32(value: i32, buffer: Uint8Array, offset: i32): void {
|
||||
buffer[offset ] = value;
|
||||
buffer[offset + 1] = value >>> 8;
|
||||
buffer[offset + 2] = value >>> 16;
|
||||
buffer[offset + 3] = value >>> 24;
|
||||
}
|
||||
|
||||
export function writeI64(value: I64, buffer: Uint8Array, offset: i32): void {
|
||||
writeI32(i64_low(value), buffer, offset);
|
||||
writeI32(i64_high(value), buffer, offset + 4);
|
||||
}
|
||||
|
||||
export function writeF32(value: f32, buffer: Uint8Array, offset: i32): void {
|
||||
writeI32(f32_as_i32(value), buffer, offset);
|
||||
}
|
||||
|
||||
export function writeF64(value: f64, buffer: Uint8Array, offset: i32): void {
|
||||
var valueI64 = f64_as_i64(value);
|
||||
writeI32(i64_low(valueI64), buffer, offset);
|
||||
writeI32(i64_high(valueI64), buffer, offset + 4);
|
||||
}
|
@ -7,3 +7,4 @@
|
||||
export * from "./charcode";
|
||||
export * from "./path";
|
||||
export * from "./text";
|
||||
export * from "./binary";
|
||||
|
Reference in New Issue
Block a user